diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..929c8209de31142c01f071256e3992ed8cc1e9f8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,147 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# Logging into tensorboard and wandb +runs/* +wandb + +# macOS +.DS_STORE + +# Local scratch work +scratch/* + +# vscode +.vscode/ + +# Don't bother tracking saved_models or videos +saved_models/* +downloaded_models/* +videos/* \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..c691634b2b31bc3198edb031b80a2c0ec7d1cd20 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Scott Goodfriend + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000000000000000000000000000000000000..8728c46dd9a3e032e504b9312e1283b2950c2f2b --- /dev/null +++ b/README.md @@ -0,0 +1,130 @@ +--- +library_name: rl-algo-impls +tags: +- PongNoFrameskip-v4 +- a2c +- deep-reinforcement-learning +- reinforcement-learning +model-index: +- name: a2c + results: + - metrics: + - type: mean_reward + value: 21.0 +/- 0.0 + name: mean_reward + task: + type: reinforcement-learning + name: reinforcement-learning + dataset: + name: PongNoFrameskip-v4 + type: PongNoFrameskip-v4 +--- +# **A2C** Agent playing **PongNoFrameskip-v4** + +This is a trained model of a **A2C** agent playing **PongNoFrameskip-v4** using the [/sgoodfriend/rl-algo-impls](https://github.com/sgoodfriend/rl-algo-impls) repo. + +All models trained at this commit can be found at https://api.wandb.ai/links/sgoodfriend/eyvb72mv. + +## Training Results + +This model was trained from 3 trainings of **A2C** agents using different initial seeds. These agents were trained by checking out [0760ef7](https://github.com/sgoodfriend/rl-algo-impls/tree/0760ef7d52b17f30219a27c18ba52c8895025ae3). The best and last models were kept from each training. This submission has loaded the best models from each training, reevaluates them, and selects the best model from these latest evaluations (mean - std). + +| algo | env | seed | reward_mean | reward_std | eval_episodes | best | wandb_url | +|:-------|:-------------------|-------:|--------------:|-------------:|----------------:|:-------|:-----------------------------------------------------------------------------| +| a2c | PongNoFrameskip-v4 | 1 | 21 | 0 | 16 | | [wandb](https://wandb.ai/sgoodfriend/rl-algo-impls-benchmarks/runs/0qr8uh8d) | +| a2c | PongNoFrameskip-v4 | 2 | 21 | 0 | 16 | | [wandb](https://wandb.ai/sgoodfriend/rl-algo-impls-benchmarks/runs/wgl9ivm1) | +| a2c | PongNoFrameskip-v4 | 3 | 21 | 0 | 16 | * | [wandb](https://wandb.ai/sgoodfriend/rl-algo-impls-benchmarks/runs/xwzoc2uw) | + + +### Prerequisites: Weights & Biases (WandB) +Training and benchmarking assumes you have a Weights & Biases project to upload runs to. +By default training goes to a rl-algo-impls project while benchmarks go to +rl-algo-impls-benchmarks. During training and benchmarking runs, videos of the best +models and the model weights are uploaded to WandB. + +Before doing anything below, you'll need to create a wandb account and run `wandb +login`. + + + +## Usage +/sgoodfriend/rl-algo-impls: https://github.com/sgoodfriend/rl-algo-impls + +Note: While the model state dictionary and hyperaparameters are saved, the latest +implementation could be sufficiently different to not be able to reproduce similar +results. You might need to checkout the commit the agent was trained on: +[0760ef7](https://github.com/sgoodfriend/rl-algo-impls/tree/0760ef7d52b17f30219a27c18ba52c8895025ae3). +``` +# Downloads the model, sets hyperparameters, and runs agent for 3 episodes +python enjoy.py --wandb-run-path=sgoodfriend/rl-algo-impls-benchmarks/xwzoc2uw +``` + +Setup hasn't been completely worked out yet, so you might be best served by using Google +Colab starting from the +[colab_enjoy.ipynb](https://github.com/sgoodfriend/rl-algo-impls/blob/main/colab_enjoy.ipynb) +notebook. + + + +## Training +If you want the highest chance to reproduce these results, you'll want to checkout the +commit the agent was trained on: [0760ef7](https://github.com/sgoodfriend/rl-algo-impls/tree/0760ef7d52b17f30219a27c18ba52c8895025ae3). While +training is deterministic, different hardware will give different results. + +``` +python train.py --algo a2c --env PongNoFrameskip-v4 --seed 3 +``` + +Setup hasn't been completely worked out yet, so you might be best served by using Google +Colab starting from the +[colab_train.ipynb](https://github.com/sgoodfriend/rl-algo-impls/blob/main/colab_train.ipynb) +notebook. + + + +## Benchmarking (with Lambda Labs instance) +This and other models from https://api.wandb.ai/links/sgoodfriend/eyvb72mv were generated by running a script on a Lambda +Labs instance. In a Lambda Labs instance terminal: +``` +git clone git@github.com:sgoodfriend/rl-algo-impls.git +cd rl-algo-impls +bash ./lambda_labs/setup.sh +wandb login +bash ./lambda_labs/benchmark.sh +``` + +### Alternative: Google Colab Pro+ +As an alternative, +[colab_benchmark.ipynb](https://github.com/sgoodfriend/rl-algo-impls/tree/main/benchmarks#:~:text=colab_benchmark.ipynb), +can be used. However, this requires a Google Colab Pro+ subscription and running across +4 separate instances because otherwise running all jobs will exceed the 24-hour limit. + + + +## Hyperparameters +This isn't exactly the format of hyperparams in hyperparams/a2c.yml, but instead the Wandb Run Config. However, it's very +close and has some additional data: +``` +algo: a2c +algo_hyperparams: + ent_coef: 0.01 + vf_coef: 0.25 +env: PongNoFrameskip-v4 +env_hyperparams: + frame_stack: 4 + n_envs: 16 + no_reward_fire_steps: 500 + no_reward_timeout_steps: 1000 + vec_env_class: async +n_timesteps: 10000000 +policy_hyperparams: + activation_fn: relu +seed: 3 +use_deterministic_algorithms: true +wandb_entity: null +wandb_project_name: rl-algo-impls-benchmarks +wandb_tags: +- benchmark_0760ef7 +- host_192-9-248-209 + +``` diff --git a/a2c/a2c.py b/a2c/a2c.py new file mode 100644 index 0000000000000000000000000000000000000000..ab4430b9b9eb61bbc99aedbae7d4dd5d44f71bd9 --- /dev/null +++ b/a2c/a2c.py @@ -0,0 +1,201 @@ +import numpy as np +import torch +import torch.nn as nn +import torch.nn.functional as F + +from dataclasses import asdict, dataclass, field +from time import perf_counter +from torch.utils.tensorboard.writer import SummaryWriter +from typing import List, Optional, Sequence, NamedTuple, TypeVar + +from shared.algorithm import Algorithm +from shared.callbacks.callback import Callback +from shared.gae import compute_advantage, compute_rtg_and_advantage +from shared.policy.on_policy import ActorCritic +from shared.schedule import schedule, update_learning_rate +from shared.stats import log_scalars +from shared.trajectory import Trajectory, TrajectoryAccumulator +from wrappers.vectorable_wrapper import ( + VecEnv, + VecEnvObs, + single_observation_space, + single_action_space, +) + +A2CSelf = TypeVar("A2CSelf", bound="A2C") + + +class A2C(Algorithm): + def __init__( + self, + policy: ActorCritic, + env: VecEnv, + device: torch.device, + tb_writer: SummaryWriter, + learning_rate: float = 7e-4, + learning_rate_decay: str = "none", + n_steps: int = 5, + gamma: float = 0.99, + gae_lambda: float = 1.0, + ent_coef: float = 0.0, + ent_coef_decay: str = "none", + vf_coef: float = 0.5, + max_grad_norm: float = 0.5, + rms_prop_eps: float = 1e-5, + use_rms_prop: bool = True, + sde_sample_freq: int = -1, + normalize_advantage: bool = False, + ) -> None: + super().__init__(policy, env, device, tb_writer) + self.policy = policy + + self.lr_schedule = schedule(learning_rate_decay, learning_rate) + if use_rms_prop: + self.optimizer = torch.optim.RMSprop( + policy.parameters(), lr=learning_rate, eps=rms_prop_eps + ) + else: + self.optimizer = torch.optim.Adam(policy.parameters(), lr=learning_rate) + + self.n_steps = n_steps + + self.gamma = gamma + self.gae_lambda = gae_lambda + + self.vf_coef = vf_coef + self.ent_coef_schedule = schedule(ent_coef_decay, ent_coef) + self.max_grad_norm = max_grad_norm + + self.sde_sample_freq = sde_sample_freq + self.normalize_advantage = normalize_advantage + + def learn( + self: A2CSelf, total_timesteps: int, callback: Optional[Callback] = None + ) -> A2CSelf: + epoch_dim = (self.n_steps, self.env.num_envs) + step_dim = (self.env.num_envs,) + obs_space = single_observation_space(self.env) + act_space = single_action_space(self.env) + + obs = np.zeros(epoch_dim + obs_space.shape, dtype=obs_space.dtype) + actions = np.zeros(epoch_dim + act_space.shape, dtype=act_space.dtype) + rewards = np.zeros(epoch_dim, dtype=np.float32) + episode_starts = np.zeros(epoch_dim, dtype=np.byte) + values = np.zeros(epoch_dim, dtype=np.float32) + logprobs = np.zeros(epoch_dim, dtype=np.float32) + + next_obs = self.env.reset() + next_episode_starts = np.ones(step_dim, dtype=np.byte) + + timesteps_elapsed = 0 + while timesteps_elapsed < total_timesteps: + start_time = perf_counter() + + progress = timesteps_elapsed / total_timesteps + ent_coef = self.ent_coef_schedule(progress) + learning_rate = self.lr_schedule(progress) + update_learning_rate(self.optimizer, learning_rate) + log_scalars( + self.tb_writer, + "charts", + { + "ent_coef": ent_coef, + "learning_rate": learning_rate, + }, + timesteps_elapsed, + ) + + self.policy.eval() + self.policy.reset_noise() + for s in range(self.n_steps): + timesteps_elapsed += self.env.num_envs + if self.sde_sample_freq > 0 and s > 0 and s % self.sde_sample_freq == 0: + self.policy.reset_noise() + + obs[s] = next_obs + episode_starts[s] = next_episode_starts + + actions[s], values[s], logprobs[s], clamped_action = self.policy.step( + next_obs + ) + next_obs, rewards[s], next_episode_starts, _ = self.env.step( + clamped_action + ) + + advantages = np.zeros(epoch_dim, dtype=np.float32) + last_gae_lam = 0 + for t in reversed(range(self.n_steps)): + if t == self.n_steps - 1: + next_nonterminal = 1.0 - next_episode_starts + next_value = self.policy.value(next_obs) + else: + next_nonterminal = 1.0 - episode_starts[t + 1] + next_value = values[t + 1] + delta = ( + rewards[t] + self.gamma * next_value * next_nonterminal - values[t] + ) + last_gae_lam = ( + delta + + self.gamma * self.gae_lambda * next_nonterminal * last_gae_lam + ) + advantages[t] = last_gae_lam + returns = advantages + values + + b_obs = torch.tensor(obs.reshape((-1,) + obs_space.shape)).to(self.device) + b_actions = torch.tensor(actions.reshape((-1,) + act_space.shape)).to( + self.device + ) + b_advantages = torch.tensor(advantages.reshape(-1)).to(self.device) + b_returns = torch.tensor(returns.reshape(-1)).to(self.device) + + if self.normalize_advantage: + b_advantages = (b_advantages - b_advantages.mean()) / ( + b_advantages.std() + 1e-8 + ) + + self.policy.train() + logp_a, entropy, v = self.policy(b_obs, b_actions) + + pi_loss = -(b_advantages * logp_a).mean() + value_loss = F.mse_loss(b_returns, v) + entropy_loss = -entropy.mean() + + loss = pi_loss + self.vf_coef * value_loss + ent_coef * entropy_loss + + self.optimizer.zero_grad() + loss.backward() + nn.utils.clip_grad_norm_(self.policy.parameters(), self.max_grad_norm) + self.optimizer.step() + + y_pred = values.reshape(-1) + y_true = returns.reshape(-1) + var_y = np.var(y_true).item() + explained_var = ( + np.nan if var_y == 0 else 1 - np.var(y_true - y_pred).item() / var_y + ) + + end_time = perf_counter() + rollout_steps = self.n_steps * self.env.num_envs + self.tb_writer.add_scalar( + "train/steps_per_second", + (rollout_steps) / (end_time - start_time), + timesteps_elapsed, + ) + + log_scalars( + self.tb_writer, + "losses", + { + "loss": loss.item(), + "pi_loss": pi_loss.item(), + "v_loss": value_loss.item(), + "entropy_loss": entropy_loss.item(), + "explained_var": explained_var, + }, + timesteps_elapsed, + ) + + if callback: + callback.on_step(timesteps_elapsed=rollout_steps) + + return self diff --git a/benchmark_publish.py b/benchmark_publish.py new file mode 100644 index 0000000000000000000000000000000000000000..55fa566837baaba44c161a2fa728120115e86d25 --- /dev/null +++ b/benchmark_publish.py @@ -0,0 +1,107 @@ +import argparse +import subprocess +import wandb +import wandb.apis.public + +from collections import defaultdict +from multiprocessing.pool import ThreadPool +from typing import List, NamedTuple + + +class RunGroup(NamedTuple): + algo: str + env_id: str + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument( + "--wandb-project-name", + type=str, + default="rl-algo-impls-benchmarks", + help="WandB project name to load runs from", + ) + parser.add_argument( + "--wandb-entity", + type=str, + default=None, + help="WandB team of project. None uses default entity", + ) + parser.add_argument("--wandb-tags", type=str, nargs="+", help="WandB tags") + parser.add_argument("--wandb-report-url", type=str, help="Link to WandB report") + parser.add_argument( + "--envs", type=str, nargs="*", help="Optional filter down to these envs" + ) + parser.add_argument( + "--exclude-envs", + type=str, + nargs="*", + help="Environments to exclude from publishing", + ) + parser.add_argument( + "--huggingface-user", + type=str, + default=None, + help="Huggingface user or team to upload model cards. Defaults to huggingface-cli login user", + ) + parser.add_argument( + "--pool-size", + type=int, + default=3, + help="How many publish jobs can run in parallel", + ) + parser.add_argument( + "--virtual-display", action="store_true", help="Use headless virtual display" + ) + # parser.set_defaults( + # wandb_tags=["benchmark_e47a44c", "host_129-146-2-230"], + # wandb_report_url="https://api.wandb.ai/links/sgoodfriend/v4wd7cp5", + # envs=[], + # exclude_envs=[], + # ) + args = parser.parse_args() + print(args) + + api = wandb.Api() + all_runs = api.runs( + f"{args.wandb_entity or api.default_entity}/{args.wandb_project_name}" + ) + + required_tags = set(args.wandb_tags) + runs: List[wandb.apis.public.Run] = [ + r + for r in all_runs + if required_tags.issubset(set(r.config.get("wandb_tags", []))) + ] + + runs_paths_by_group = defaultdict(list) + for r in runs: + if r.state != "finished": + continue + algo = r.config["algo"] + env = r.config["env"] + if args.envs and env not in args.envs: + continue + if args.exclude_envs and env in args.exclude_envs: + continue + run_group = RunGroup(algo, env) + runs_paths_by_group[run_group].append("/".join(r.path)) + + def run(run_paths: List[str]) -> None: + publish_args = ["python", "huggingface_publish.py"] + publish_args.append("--wandb-run-paths") + publish_args.extend(run_paths) + publish_args.append("--wandb-report-url") + publish_args.append(args.wandb_report_url) + if args.huggingface_user: + publish_args.append("--huggingface-user") + publish_args.append(args.huggingface_user) + if args.virtual_display: + publish_args.append("--virtual-display") + subprocess.run(publish_args) + + tp = ThreadPool(args.pool_size) + for run_paths in runs_paths_by_group.values(): + tp.apply_async(run, (run_paths,)) + tp.close() + tp.join() diff --git a/benchmarks/benchmark_test.sh b/benchmarks/benchmark_test.sh new file mode 100644 index 0000000000000000000000000000000000000000..5ab766df564f86a5dad2737dfb96a421bc48067c --- /dev/null +++ b/benchmarks/benchmark_test.sh @@ -0,0 +1,32 @@ +source benchmarks/train_loop.sh + +export WANDB_PROJECT_NAME="rl-algo-impls" + +BENCHMARK_MAX_PROCS="${BENCHMARK_MAX_PROCS:-3}" + +ALGOS=( + # "vpg" + "dqn" + # "ppo" +) +ENVS=( + # Basic + "CartPole-v1" + "MountainCar-v0" + # "MountainCarContinuous-v0" + "Acrobot-v1" + "LunarLander-v2" + # # PyBullet + # "HalfCheetahBulletEnv-v0" + # "AntBulletEnv-v0" + # "HopperBulletEnv-v0" + # "Walker2DBulletEnv-v0" + # # CarRacing + # "CarRacing-v0" + # Atari + "PongNoFrameskip-v4" + "BreakoutNoFrameskip-v4" + "SpaceInvadersNoFrameskip-v4" + "QbertNoFrameskip-v4" +) +train_loop "${ALGOS[*]}" "${ENVS[*]}" | xargs -I CMD -P $BENCHMARK_MAX_PROCS bash -c CMD diff --git a/benchmarks/colab_atari1.sh b/benchmarks/colab_atari1.sh new file mode 100644 index 0000000000000000000000000000000000000000..ac3c646314420c360191f50135510c75e720a810 --- /dev/null +++ b/benchmarks/colab_atari1.sh @@ -0,0 +1,5 @@ +source benchmarks/train_loop.sh +ALGOS="ppo" +ENVS="PongNoFrameskip-v4 BreakoutNoFrameskip-v4" +BENCHMARK_MAX_PROCS="${BENCHMARK_MAX_PROCS:-3}" +train_loop $ALGOS "$ENVS" | xargs -I CMD -P $BENCHMARK_MAX_PROCS bash -c CMD \ No newline at end of file diff --git a/benchmarks/colab_atari2.sh b/benchmarks/colab_atari2.sh new file mode 100644 index 0000000000000000000000000000000000000000..5e7b79e72b626a8e93f635a45346670e07b20c34 --- /dev/null +++ b/benchmarks/colab_atari2.sh @@ -0,0 +1,5 @@ +source benchmarks/train_loop.sh +ALGOS="ppo" +ENVS="SpaceInvadersNoFrameskip-v4 QbertNoFrameskip-v4" +BENCHMARK_MAX_PROCS="${BENCHMARK_MAX_PROCS:-3}" +train_loop $ALGOS "$ENVS" | xargs -I CMD -P $BENCHMARK_MAX_PROCS bash -c CMD \ No newline at end of file diff --git a/benchmarks/colab_basic.sh b/benchmarks/colab_basic.sh new file mode 100644 index 0000000000000000000000000000000000000000..affe598a08c94ea23cecacfab44931170ecfd29f --- /dev/null +++ b/benchmarks/colab_basic.sh @@ -0,0 +1,5 @@ +source benchmarks/train_loop.sh +ALGOS="ppo" +ENVS="CartPole-v1 MountainCar-v0 MountainCarContinuous-v0 Acrobot-v1 LunarLander-v2" +BENCHMARK_MAX_PROCS="${BENCHMARK_MAX_PROCS:-3}" +train_loop $ALGOS "$ENVS" | xargs -I CMD -P $BENCHMARK_MAX_PROCS bash -c CMD diff --git a/benchmarks/colab_benchmark.ipynb b/benchmarks/colab_benchmark.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..a331841fd9fdd9b85c029c9c573652381d003483 --- /dev/null +++ b/benchmarks/colab_benchmark.ipynb @@ -0,0 +1,195 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "provenance": [], + "machine_shape": "hm", + "authorship_tag": "ABX9TyOGIH7rqgasim3Sz7b1rpoE", + "include_colab_link": true + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + }, + "language_info": { + "name": "python" + }, + "gpuClass": "standard", + "accelerator": "GPU" + }, + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "view-in-github", + "colab_type": "text" + }, + "source": [ + "\"Open" + ] + }, + { + "cell_type": "markdown", + "source": [ + "# [sgoodfriend/rl-algo-impls](https://github.com/sgoodfriend/rl-algo-impls) in Google Colaboratory\n", + "## Parameters\n", + "\n", + "\n", + "1. Wandb\n", + "\n" + ], + "metadata": { + "id": "S-tXDWP8WTLc" + } + }, + { + "cell_type": "code", + "source": [ + "from getpass import getpass\n", + "import os\n", + "os.environ[\"WANDB_API_KEY\"] = getpass(\"Wandb API key to upload metrics, videos, and models: \")" + ], + "metadata": { + "id": "1ZtdYgxWNGwZ" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "## Setup\n", + "Clone [sgoodfriend/rl-algo-impls](https://github.com/sgoodfriend/rl-algo-impls) " + ], + "metadata": { + "id": "bsG35Io0hmKG" + } + }, + { + "cell_type": "code", + "source": [ + "%%capture\n", + "!git clone https://github.com/sgoodfriend/rl-algo-impls.git" + ], + "metadata": { + "id": "k5ynTV25hdAf" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "Installing the correct packages:\n", + "\n", + "While conda and poetry are generally used for package management, the mismatch in Python versions (3.10 in the project file vs 3.8 in Colab) makes using the package yml files difficult to use. For now, instead I'm going to specify the list of requirements manually below:" + ], + "metadata": { + "id": "jKxGok-ElYQ7" + } + }, + { + "cell_type": "code", + "source": [ + "%%capture\n", + "!apt install python-opengl\n", + "!apt install ffmpeg\n", + "!apt install xvfb\n", + "!apt install swig" + ], + "metadata": { + "id": "nn6EETTc2Ewf" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "%%capture\n", + "%cd /content/rl-algo-impls\n", + "!pip install -r colab_requirements.txt" + ], + "metadata": { + "id": "AfZh9rH3yQii" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "## Run Once Per Runtime" + ], + "metadata": { + "id": "4o5HOLjc4wq7" + } + }, + { + "cell_type": "code", + "source": [ + "import wandb\n", + "wandb.login()" + ], + "metadata": { + "id": "PCXa5tdS2qFX" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "## Restart Session beteween runs" + ], + "metadata": { + "id": "AZBZfSUV43JQ" + } + }, + { + "cell_type": "code", + "source": [ + "%%capture\n", + "from pyvirtualdisplay import Display\n", + "\n", + "virtual_display = Display(visible=0, size=(1400, 900))\n", + "virtual_display.start()" + ], + "metadata": { + "id": "VzemeQJP2NO9" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "The below 5 bash scripts train agents on environments with 3 seeds each:\n", + "- colab_basic.sh and colab_pybullet.sh test on a set of basic gym environments and 4 PyBullet environments. Running both together will likely take about 18 hours. This is likely to run into runtime limits for free Colab and Colab Pro, but is fine for Colab Pro+.\n", + "- colab_carracing.sh only trains 3 seeds on CarRacing-v0, which takes almost 22 hours on Colab Pro+ on high-RAM, standard GPU.\n", + "- colab_atari1.sh and colab_atari2.sh likely need to be run separately because each takes about 19 hours on high-RAM, standard GPU." + ], + "metadata": { + "id": "nSHfna0hLlO1" + } + }, + { + "cell_type": "code", + "source": [ + "%cd /content/rl-algo-impls\n", + "os.environ[\"BENCHMARK_MAX_PROCS\"] = str(1) # Can't reliably raise this to 2+, but would make it faster.\n", + "!./benchmarks/colab_basic.sh\n", + "!./benchmarks/colab_pybullet.sh\n", + "# !./benchmarks/colab_carracing.sh\n", + "# !./benchmarks/colab_atari1.sh\n", + "# !./benchmarks/colab_atari2.sh" + ], + "metadata": { + "id": "07aHYFH1zfXa" + }, + "execution_count": null, + "outputs": [] + } + ] +} \ No newline at end of file diff --git a/benchmarks/colab_carracing.sh b/benchmarks/colab_carracing.sh new file mode 100644 index 0000000000000000000000000000000000000000..7101140eba9fa2ee8b953ad07d7662fba47416f9 --- /dev/null +++ b/benchmarks/colab_carracing.sh @@ -0,0 +1,5 @@ +source benchmarks/train_loop.sh +ALGOS="ppo" +ENVS="CarRacing-v0" +BENCHMARK_MAX_PROCS="${BENCHMARK_MAX_PROCS:-3}" +train_loop $ALGOS "$ENVS" | xargs -I CMD -P $BENCHMARK_MAX_PROCS bash -c CMD \ No newline at end of file diff --git a/benchmarks/colab_pybullet.sh b/benchmarks/colab_pybullet.sh new file mode 100644 index 0000000000000000000000000000000000000000..69e36bac610748c5bd18f5a58a3e79bd39b8b181 --- /dev/null +++ b/benchmarks/colab_pybullet.sh @@ -0,0 +1,5 @@ +source benchmarks/train_loop.sh +ALGOS="ppo" +ENVS="HalfCheetahBulletEnv-v0 AntBulletEnv-v0 HopperBulletEnv-v0 Walker2DBulletEnv-v0" +BENCHMARK_MAX_PROCS="${BENCHMARK_MAX_PROCS:-3}" +train_loop $ALGOS "$ENVS" | xargs -I CMD -P $BENCHMARK_MAX_PROCS bash -c CMD \ No newline at end of file diff --git a/benchmarks/train_loop.sh b/benchmarks/train_loop.sh new file mode 100644 index 0000000000000000000000000000000000000000..9ad750dcf09b706a045ed24cbc2c0a91575a6b4e --- /dev/null +++ b/benchmarks/train_loop.sh @@ -0,0 +1,15 @@ +train_loop () { + local WANDB_TAGS="benchmark_$(git rev-parse --short HEAD) host_$(hostname)" + local algo + local env + local seed + local WANDB_PROJECT_NAME="${WANDB_PROJECT_NAME:-rl-algo-impls-benchmarks}" + local SEEDS="${SEEDS:-1 2 3}" + for algo in $(echo $1); do + for env in $(echo $2); do + for seed in $SEEDS; do + echo python train.py --algo $algo --env $env --seed $seed --pool-size 1 --wandb-tags $WANDB_TAGS --wandb-project-name $WANDB_PROJECT_NAME + done + done + done +} \ No newline at end of file diff --git a/colab_enjoy.ipynb b/colab_enjoy.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..57daecf4ee497e489387bbb338cd2baa4b01a67a --- /dev/null +++ b/colab_enjoy.ipynb @@ -0,0 +1,198 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "provenance": [], + "machine_shape": "hm", + "authorship_tag": "ABX9TyN6S7kyJKrM5x0OOiN+CgTc", + "include_colab_link": true + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + }, + "language_info": { + "name": "python" + }, + "gpuClass": "standard", + "accelerator": "GPU" + }, + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "view-in-github", + "colab_type": "text" + }, + "source": [ + "\"Open" + ] + }, + { + "cell_type": "markdown", + "source": [ + "# [sgoodfriend/rl-algo-impls](https://github.com/sgoodfriend/rl-algo-impls) in Google Colaboratory\n", + "## Parameters\n", + "\n", + "\n", + "1. Wandb\n", + "\n" + ], + "metadata": { + "id": "S-tXDWP8WTLc" + } + }, + { + "cell_type": "code", + "source": [ + "from getpass import getpass\n", + "import os\n", + "os.environ[\"WANDB_API_KEY\"] = getpass(\"Wandb API key to upload metrics, videos, and models: \")" + ], + "metadata": { + "id": "1ZtdYgxWNGwZ" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "2. enjoy.py parameters" + ], + "metadata": { + "id": "ao0nAh3MOdN7" + } + }, + { + "cell_type": "code", + "source": [ + "WANDB_RUN_PATH=\"sgoodfriend/rl-algo-impls-benchmarks/rd0lisee\"" + ], + "metadata": { + "id": "jKL_NFhVOjSc" + }, + "execution_count": 2, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "## Setup\n", + "Clone [sgoodfriend/rl-algo-impls](https://github.com/sgoodfriend/rl-algo-impls) " + ], + "metadata": { + "id": "bsG35Io0hmKG" + } + }, + { + "cell_type": "code", + "source": [ + "%%capture\n", + "!git clone https://github.com/sgoodfriend/rl-algo-impls.git" + ], + "metadata": { + "id": "k5ynTV25hdAf" + }, + "execution_count": 3, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "Installing the correct packages:\n", + "\n", + "While conda and poetry are generally used for package management, the mismatch in Python versions (3.10 in the project file vs 3.8 in Colab) makes using the package yml files difficult to use. For now, instead I'm going to specify the list of requirements manually below:" + ], + "metadata": { + "id": "jKxGok-ElYQ7" + } + }, + { + "cell_type": "code", + "source": [ + "%%capture\n", + "!apt install python-opengl\n", + "!apt install ffmpeg\n", + "!apt install xvfb\n", + "!apt install swig" + ], + "metadata": { + "id": "nn6EETTc2Ewf" + }, + "execution_count": 4, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "%%capture\n", + "%cd /content/rl-algo-impls\n", + "!pip install -r colab_requirements.txt" + ], + "metadata": { + "id": "AfZh9rH3yQii" + }, + "execution_count": 5, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "## Run Once Per Runtime" + ], + "metadata": { + "id": "4o5HOLjc4wq7" + } + }, + { + "cell_type": "code", + "source": [ + "import wandb\n", + "wandb.login()" + ], + "metadata": { + "id": "PCXa5tdS2qFX" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "## Restart Session beteween runs" + ], + "metadata": { + "id": "AZBZfSUV43JQ" + } + }, + { + "cell_type": "code", + "source": [ + "%%capture\n", + "from pyvirtualdisplay import Display\n", + "\n", + "virtual_display = Display(visible=0, size=(1400, 900))\n", + "virtual_display.start()" + ], + "metadata": { + "id": "VzemeQJP2NO9" + }, + "execution_count": 7, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "%cd /content/rl-algo-impls\n", + "!python enjoy.py --wandb-run-path={WANDB_RUN_PATH}" + ], + "metadata": { + "id": "07aHYFH1zfXa" + }, + "execution_count": null, + "outputs": [] + } + ] +} \ No newline at end of file diff --git a/colab_requirements.txt b/colab_requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..798b0a43c5831756303e88f525d1f54bf3384851 --- /dev/null +++ b/colab_requirements.txt @@ -0,0 +1,14 @@ +AutoROM.accept-rom-license >= 0.4.2, < 0.5 +stable-baselines3[extra] >= 1.7.0, < 1.8 +gym[box2d] >= 0.21.0, < 0.22 +pyglet == 1.5.27 +wandb >= 0.13.10, < 0.14 +pyvirtualdisplay == 3.0 +pybullet >= 3.2.5, < 3.3 +tabulate >= 0.9.0, < 0.10 +huggingface-hub >= 0.12.0, < 0.13 +numexpr >= 2.8.4, < 2.9 +gym3 >= 0.3.3, < 0.4 +glfw >= 1.12.0, < 1.13 +procgen >= 0.10.7, < 0.11 +ipython >= 8.10.0, < 8.11 \ No newline at end of file diff --git a/colab_train.ipynb b/colab_train.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..7f981493d0545cfabd5b73db13f1f3bb30114855 --- /dev/null +++ b/colab_train.ipynb @@ -0,0 +1,200 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "provenance": [], + "machine_shape": "hm", + "authorship_tag": "ABX9TyMmemQnx6G7GOnn6XBdjgxY", + "include_colab_link": true + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + }, + "language_info": { + "name": "python" + }, + "gpuClass": "standard", + "accelerator": "GPU" + }, + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "view-in-github", + "colab_type": "text" + }, + "source": [ + "\"Open" + ] + }, + { + "cell_type": "markdown", + "source": [ + "# [sgoodfriend/rl-algo-impls](https://github.com/sgoodfriend/rl-algo-impls) in Google Colaboratory\n", + "## Parameters\n", + "\n", + "\n", + "1. Wandb\n", + "\n" + ], + "metadata": { + "id": "S-tXDWP8WTLc" + } + }, + { + "cell_type": "code", + "source": [ + "from getpass import getpass\n", + "import os\n", + "os.environ[\"WANDB_API_KEY\"] = getpass(\"Wandb API key to upload metrics, videos, and models: \")" + ], + "metadata": { + "id": "1ZtdYgxWNGwZ" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "2. train run parameters" + ], + "metadata": { + "id": "ao0nAh3MOdN7" + } + }, + { + "cell_type": "code", + "source": [ + "ALGO = \"ppo\"\n", + "ENV = \"CartPole-v1\"\n", + "SEED = 1" + ], + "metadata": { + "id": "jKL_NFhVOjSc" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "## Setup\n", + "Clone [sgoodfriend/rl-algo-impls](https://github.com/sgoodfriend/rl-algo-impls) " + ], + "metadata": { + "id": "bsG35Io0hmKG" + } + }, + { + "cell_type": "code", + "source": [ + "%%capture\n", + "!git clone https://github.com/sgoodfriend/rl-algo-impls.git" + ], + "metadata": { + "id": "k5ynTV25hdAf" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "Installing the correct packages:\n", + "\n", + "While conda and poetry are generally used for package management, the mismatch in Python versions (3.10 in the project file vs 3.8 in Colab) makes using the package yml files difficult to use. For now, instead I'm going to specify the list of requirements manually below:" + ], + "metadata": { + "id": "jKxGok-ElYQ7" + } + }, + { + "cell_type": "code", + "source": [ + "%%capture\n", + "!apt install python-opengl\n", + "!apt install ffmpeg\n", + "!apt install xvfb\n", + "!apt install swig" + ], + "metadata": { + "id": "nn6EETTc2Ewf" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "%%capture\n", + "%cd /content/rl-algo-impls\n", + "!pip install -r colab_requirements.txt" + ], + "metadata": { + "id": "AfZh9rH3yQii" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "## Run Once Per Runtime" + ], + "metadata": { + "id": "4o5HOLjc4wq7" + } + }, + { + "cell_type": "code", + "source": [ + "import wandb\n", + "wandb.login()" + ], + "metadata": { + "id": "PCXa5tdS2qFX" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "## Restart Session beteween runs" + ], + "metadata": { + "id": "AZBZfSUV43JQ" + } + }, + { + "cell_type": "code", + "source": [ + "%%capture\n", + "from pyvirtualdisplay import Display\n", + "\n", + "virtual_display = Display(visible=0, size=(1400, 900))\n", + "virtual_display.start()" + ], + "metadata": { + "id": "VzemeQJP2NO9" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "%cd /content/rl-algo-impls\n", + "!python train.py --algo {ALGO} --env {ENV} --seed {SEED}" + ], + "metadata": { + "id": "07aHYFH1zfXa" + }, + "execution_count": null, + "outputs": [] + } + ] +} \ No newline at end of file diff --git a/compare_runs.py b/compare_runs.py new file mode 100644 index 0000000000000000000000000000000000000000..d92a62dfbfa10a208cc6f64c62fd45a300004071 --- /dev/null +++ b/compare_runs.py @@ -0,0 +1,187 @@ +import argparse +import itertools +import numpy as np +import pandas as pd +import wandb +import wandb.apis.public + +from collections import defaultdict +from dataclasses import dataclass +from typing import Dict, Iterable, List, TypeVar + +from benchmark_publish import RunGroup + + +@dataclass +class Comparison: + control_values: List[float] + experiment_values: List[float] + + def mean_diff_percentage(self) -> float: + return self._diff_percentage( + np.mean(self.control_values).item(), np.mean(self.experiment_values).item() + ) + + def median_diff_percentage(self) -> float: + return self._diff_percentage( + np.median(self.control_values).item(), + np.median(self.experiment_values).item(), + ) + + def _diff_percentage(self, c: float, e: float) -> float: + if c == e: + return 0 + elif c == 0: + return float("inf") if e > 0 else float("-inf") + return 100 * (e - c) / c + + def score(self) -> float: + return ( + np.sum( + np.sign((self.mean_diff_percentage(), self.median_diff_percentage())) + ).item() + / 2 + ) + + +RunGroupRunsSelf = TypeVar("RunGroupRunsSelf", bound="RunGroupRuns") + + +class RunGroupRuns: + def __init__( + self, + run_group: RunGroup, + control: List[str], + experiment: List[str], + summary_stats: List[str] = ["best_eval", "eval", "train_rolling"], + summary_metrics: List[str] = ["mean", "result"], + ) -> None: + self.algo = run_group.algo + self.env = run_group.env_id + self.control = set(control) + self.experiment = set(experiment) + + self.summary_stats = summary_stats + self.summary_metrics = summary_metrics + + self.control_runs = [] + self.experiment_runs = [] + + def add_run(self, run: wandb.apis.public.Run) -> None: + wandb_tags = set(run.config.get("wandb_tags", [])) + if self.control & wandb_tags: + self.control_runs.append(run) + elif self.experiment & wandb_tags: + self.experiment_runs.append(run) + + def comparisons_by_metric(self) -> Dict[str, Comparison]: + c_by_m = {} + for metric in ( + f"{s}/{m}" + for s, m in itertools.product(self.summary_stats, self.summary_metrics) + ): + c_by_m[metric] = Comparison( + [c.summary[metric] for c in self.control_runs], + [e.summary[metric] for e in self.experiment_runs], + ) + return c_by_m + + @staticmethod + def data_frame(rows: Iterable[RunGroupRunsSelf]) -> pd.DataFrame: + results = defaultdict(list) + for r in rows: + if not r.control_runs or not r.experiment_runs: + continue + results["algo"].append(r.algo) + results["env"].append(r.env) + results["control"].append(r.control) + results["expierment"].append(r.experiment) + c_by_m = r.comparisons_by_metric() + results["score"].append( + sum(m.score() for m in c_by_m.values()) / len(c_by_m) + ) + for m, c in c_by_m.items(): + results[f"{m}_mean"].append(c.mean_diff_percentage()) + results[f"{m}_median"].append(c.median_diff_percentage()) + return pd.DataFrame(results) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument( + "-p", + "--wandb-project-name", + type=str, + default="rl-algo-impls-benchmarks", + help="WandB project name to load runs from", + ) + parser.add_argument( + "--wandb-entity", + type=str, + default=None, + help="WandB team. None uses default entity", + ) + parser.add_argument( + "-n", + "--wandb-hostname-tag", + type=str, + nargs="*", + help="WandB tags for hostname (i.e. host_192-9-145-26)", + ) + parser.add_argument( + "-c", + "--wandb-control-tag", + type=str, + nargs="+", + help="WandB tag for control commit (i.e. benchmark_5598ebc)", + ) + parser.add_argument( + "-e", + "--wandb-experiment-tag", + type=str, + nargs="+", + help="WandB tag for experiment commit (i.e. benchmark_5540e1f)", + ) + parser.add_argument( + "--exclude-envs", + type=str, + nargs="*", + help="Environments to exclude from comparison", + ) + # parser.set_defaults( + # wandb_hostname_tag=["host_150-230-44-105", "host_155-248-214-128"], + # wandb_control_tag=["benchmark_fbc943f"], + # wandb_experiment_tag=["benchmark_f59bf74"], + # exclude_envs=[], + # ) + args = parser.parse_args() + print(args) + + api = wandb.Api() + all_runs = api.runs( + path=f"{args.wandb_entity or api.default_entity}/{args.wandb_project_name}", + order="+created_at", + ) + + runs_by_run_group: Dict[RunGroup, RunGroupRuns] = {} + wandb_hostname_tags = set(args.wandb_hostname_tag) + for r in all_runs: + if r.state != "finished": + continue + wandb_tags = set(r.config.get("wandb_tags", [])) + if not wandb_tags or not wandb_hostname_tags & wandb_tags: + continue + rg = RunGroup(r.config["algo"], r.config.get("env_id") or r.config["env"]) + if args.exclude_envs and rg.env_id in args.exclude_envs: + continue + if rg not in runs_by_run_group: + runs_by_run_group[rg] = RunGroupRuns( + rg, + args.wandb_control_tag, + args.wandb_experiment_tag, + ) + runs_by_run_group[rg].add_run(r) + df = RunGroupRuns.data_frame(runs_by_run_group.values()).round(decimals=2) + print(f"**Total Score: {sum(df.score)}**") + df.loc["mean"] = df.mean(numeric_only=True) + print(df.to_markdown()) diff --git a/dqn/dqn.py b/dqn/dqn.py new file mode 100644 index 0000000000000000000000000000000000000000..d057178294f661ff8ea83a1b023943480b6faf9e --- /dev/null +++ b/dqn/dqn.py @@ -0,0 +1,182 @@ +import copy +import numpy as np +import random +import torch +import torch.nn as nn +import torch.nn.functional as F + +from collections import deque +from torch.optim import Adam +from torch.utils.tensorboard.writer import SummaryWriter +from typing import List, NamedTuple, Optional, TypeVar + +from dqn.policy import DQNPolicy +from shared.algorithm import Algorithm +from shared.callbacks.callback import Callback +from shared.schedule import linear_schedule +from wrappers.vectorable_wrapper import VecEnv, VecEnvObs + + +class Transition(NamedTuple): + obs: np.ndarray + action: np.ndarray + reward: float + done: bool + next_obs: np.ndarray + + +class Batch(NamedTuple): + obs: np.ndarray + actions: np.ndarray + rewards: np.ndarray + dones: np.ndarray + next_obs: np.ndarray + + +class ReplayBuffer: + def __init__(self, num_envs: int, maxlen: int) -> None: + self.num_envs = num_envs + self.buffer = deque(maxlen=maxlen) + + def add( + self, + obs: VecEnvObs, + action: np.ndarray, + reward: np.ndarray, + done: np.ndarray, + next_obs: VecEnvObs, + ) -> None: + assert isinstance(obs, np.ndarray) + assert isinstance(next_obs, np.ndarray) + for i in range(self.num_envs): + self.buffer.append( + Transition(obs[i], action[i], reward[i], done[i], next_obs[i]) + ) + + def sample(self, batch_size: int) -> Batch: + ts = random.sample(self.buffer, batch_size) + return Batch( + obs=np.array([t.obs for t in ts]), + actions=np.array([t.action for t in ts]), + rewards=np.array([t.reward for t in ts]), + dones=np.array([t.done for t in ts]), + next_obs=np.array([t.next_obs for t in ts]), + ) + + def __len__(self) -> int: + return len(self.buffer) + + +DQNSelf = TypeVar("DQNSelf", bound="DQN") + + +class DQN(Algorithm): + def __init__( + self, + policy: DQNPolicy, + env: VecEnv, + device: torch.device, + tb_writer: SummaryWriter, + learning_rate: float = 1e-4, + buffer_size: int = 1_000_000, + learning_starts: int = 50_000, + batch_size: int = 32, + tau: float = 1.0, + gamma: float = 0.99, + train_freq: int = 4, + gradient_steps: int = 1, + target_update_interval: int = 10_000, + exploration_fraction: float = 0.1, + exploration_initial_eps: float = 1.0, + exploration_final_eps: float = 0.05, + max_grad_norm: float = 10.0, + ) -> None: + super().__init__(policy, env, device, tb_writer) + self.policy = policy + + self.optimizer = Adam(self.policy.q_net.parameters(), lr=learning_rate) + + self.target_q_net = copy.deepcopy(self.policy.q_net).to(self.device) + self.target_q_net.train(False) + self.tau = tau + self.target_update_interval = target_update_interval + + self.replay_buffer = ReplayBuffer(self.env.num_envs, buffer_size) + self.batch_size = batch_size + + self.learning_starts = learning_starts + self.train_freq = train_freq + self.gradient_steps = gradient_steps + + self.gamma = gamma + self.exploration_eps_schedule = linear_schedule( + exploration_initial_eps, + exploration_final_eps, + end_fraction=exploration_fraction, + ) + + self.max_grad_norm = max_grad_norm + + def learn( + self: DQNSelf, total_timesteps: int, callback: Optional[Callback] = None + ) -> DQNSelf: + self.policy.train(True) + obs = self.env.reset() + obs = self._collect_rollout(self.learning_starts, obs, 1) + learning_steps = total_timesteps - self.learning_starts + timesteps_elapsed = 0 + steps_since_target_update = 0 + while timesteps_elapsed < learning_steps: + progress = timesteps_elapsed / learning_steps + eps = self.exploration_eps_schedule(progress) + obs = self._collect_rollout(self.train_freq, obs, eps) + rollout_steps = self.train_freq + timesteps_elapsed += rollout_steps + for _ in range( + self.gradient_steps if self.gradient_steps > 0 else self.train_freq + ): + self.train() + steps_since_target_update += rollout_steps + if steps_since_target_update >= self.target_update_interval: + self._update_target() + steps_since_target_update = 0 + if callback: + callback.on_step(timesteps_elapsed=rollout_steps) + return self + + def train(self) -> None: + if len(self.replay_buffer) < self.batch_size: + return + o, a, r, d, next_o = self.replay_buffer.sample(self.batch_size) + o = torch.as_tensor(o, device=self.device) + a = torch.as_tensor(a, device=self.device).unsqueeze(1) + r = torch.as_tensor(r, dtype=torch.float32, device=self.device) + d = torch.as_tensor(d, dtype=torch.long, device=self.device) + next_o = torch.as_tensor(next_o, device=self.device) + + with torch.no_grad(): + target = r + (1 - d) * self.gamma * self.target_q_net(next_o).max(1).values + current = self.policy.q_net(o).gather(dim=1, index=a).squeeze(1) + loss = F.smooth_l1_loss(current, target) + + self.optimizer.zero_grad() + loss.backward() + if self.max_grad_norm: + nn.utils.clip_grad_norm_(self.policy.q_net.parameters(), self.max_grad_norm) + self.optimizer.step() + + def _collect_rollout(self, timesteps: int, obs: VecEnvObs, eps: float) -> VecEnvObs: + for _ in range(0, timesteps, self.env.num_envs): + action = self.policy.act(obs, eps, deterministic=False) + next_obs, reward, done, _ = self.env.step(action) + self.replay_buffer.add(obs, action, reward, done, next_obs) + obs = next_obs + return obs + + def _update_target(self) -> None: + for target_param, param in zip( + self.target_q_net.parameters(), self.policy.q_net.parameters() + ): + target_param.data.copy_( + self.tau * param.data + (1 - self.tau) * target_param.data + ) diff --git a/dqn/policy.py b/dqn/policy.py new file mode 100644 index 0000000000000000000000000000000000000000..ea6c28b5db004676e0bd22eccc3827e781b7b1a8 --- /dev/null +++ b/dqn/policy.py @@ -0,0 +1,52 @@ +import numpy as np +import os +import torch + +from typing import Optional, Sequence, TypeVar + +from dqn.q_net import QNetwork +from shared.policy.policy import Policy +from wrappers.vectorable_wrapper import ( + VecEnv, + VecEnvObs, + single_observation_space, + single_action_space, +) + +DQNPolicySelf = TypeVar("DQNPolicySelf", bound="DQNPolicy") + + +class DQNPolicy(Policy): + def __init__( + self, + env: VecEnv, + hidden_sizes: Sequence[int] = [], + cnn_feature_dim: int = 512, + cnn_style: str = "nature", + cnn_layers_init_orthogonal: Optional[bool] = None, + impala_channels: Sequence[int] = (16, 32, 32), + **kwargs, + ) -> None: + super().__init__(env, **kwargs) + self.q_net = QNetwork( + single_observation_space(env), + single_action_space(env), + hidden_sizes, + cnn_feature_dim=cnn_feature_dim, + cnn_style=cnn_style, + cnn_layers_init_orthogonal=cnn_layers_init_orthogonal, + impala_channels=impala_channels, + ) + + def act( + self, obs: VecEnvObs, eps: float = 0, deterministic: bool = True + ) -> np.ndarray: + assert eps == 0 if deterministic else eps >= 0 + if not deterministic and np.random.random() < eps: + return np.array( + [self.env.action_space.sample() for _ in range(self.env.num_envs)] + ) + else: + o = self._as_tensor(obs) + with torch.no_grad(): + return self.q_net(o).argmax(axis=1).cpu().numpy() diff --git a/dqn/q_net.py b/dqn/q_net.py new file mode 100644 index 0000000000000000000000000000000000000000..37859f0c22708392713c95d87964de2c494cbeb9 --- /dev/null +++ b/dqn/q_net.py @@ -0,0 +1,41 @@ +import gym +import torch as th +import torch.nn as nn + +from gym.spaces import Discrete +from typing import Optional, Sequence, Type + +from shared.module.feature_extractor import FeatureExtractor +from shared.module.module import mlp + + +class QNetwork(nn.Module): + def __init__( + self, + observation_space: gym.Space, + action_space: gym.Space, + hidden_sizes: Sequence[int] = [], + activation: Type[nn.Module] = nn.ReLU, # Used by stable-baselines3 + cnn_feature_dim: int = 512, + cnn_style: str = "nature", + cnn_layers_init_orthogonal: Optional[bool] = None, + impala_channels: Sequence[int] = (16, 32, 32), + ) -> None: + super().__init__() + assert isinstance(action_space, Discrete) + self._feature_extractor = FeatureExtractor( + observation_space, + activation, + cnn_feature_dim=cnn_feature_dim, + cnn_style=cnn_style, + cnn_layers_init_orthogonal=cnn_layers_init_orthogonal, + impala_channels=impala_channels, + ) + layer_sizes = ( + (self._feature_extractor.out_dim,) + tuple(hidden_sizes) + (action_space.n,) + ) + self._fc = mlp(layer_sizes, activation) + + def forward(self, obs: th.Tensor) -> th.Tensor: + x = self._feature_extractor(obs) + return self._fc(x) diff --git a/enjoy.py b/enjoy.py new file mode 100644 index 0000000000000000000000000000000000000000..fd004de69152d374d292d4910e9854364a4a08e5 --- /dev/null +++ b/enjoy.py @@ -0,0 +1,30 @@ +# Support for PyTorch mps mode (https://pytorch.org/docs/stable/notes/mps.html) +import os + +os.environ["PYTORCH_ENABLE_MPS_FALLBACK"] = "1" + +from runner.evaluate import EvalArgs, evaluate_model +from runner.running_utils import base_parser + + +if __name__ == "__main__": + parser = base_parser(multiple=False) + parser.add_argument("--render", default=True, type=bool) + parser.add_argument("--best", default=True, type=bool) + parser.add_argument("--n_envs", default=1, type=int) + parser.add_argument("--n_episodes", default=3, type=int) + parser.add_argument("--deterministic-eval", default=None, type=bool) + parser.add_argument( + "--no-print-returns", action="store_true", help="Limit printing" + ) + # wandb-run-path overrides base RunArgs + parser.add_argument("--wandb-run-path", default=None, type=str) + parser.set_defaults( + algo=["ppo"], + ) + args = parser.parse_args() + args.algo = args.algo[0] + args.env = args.env[0] + args = EvalArgs(**vars(args)) + + evaluate_model(args, os.path.dirname(__file__)) diff --git a/environment.yml b/environment.yml new file mode 100644 index 0000000000000000000000000000000000000000..969f19408426ae75cf64779f01acd2418adbfd85 --- /dev/null +++ b/environment.yml @@ -0,0 +1,17 @@ +name: rl_algo_impls +channels: + - pytorch + - conda-forge + - nodefaults +dependencies: + - python=3.10.* + - mamba + - pip + - poetry + - pytorch + - torchvision + - torchaudio + - cmake + - swig + - ipywidgets + - black diff --git a/huggingface_publish.py b/huggingface_publish.py new file mode 100644 index 0000000000000000000000000000000000000000..cc481c73f978df4af466228d4e1d9ec8701c5398 --- /dev/null +++ b/huggingface_publish.py @@ -0,0 +1,189 @@ +import os + +os.environ["PYTORCH_ENABLE_MPS_FALLBACK"] = "1" + +import argparse +import requests +import shutil +import subprocess +import tempfile +import wandb +import wandb.apis.public + +from typing import List, Optional + +from huggingface_hub.hf_api import HfApi, upload_folder +from huggingface_hub.repocard import metadata_save +from pyvirtualdisplay.display import Display + +from publish.markdown_format import EvalTableData, model_card_text +from runner.config import EnvHyperparams +from runner.evaluate import EvalArgs, evaluate_model +from runner.env import make_eval_env +from shared.callbacks.eval_callback import evaluate +from wrappers.vec_episode_recorder import VecEpisodeRecorder + + +def publish( + wandb_run_paths: List[str], + wandb_report_url: str, + huggingface_user: Optional[str] = None, + huggingface_token: Optional[str] = None, + virtual_display: bool = False, +) -> None: + if virtual_display: + display = Display(visible=False, size=(1400, 900)) + display.start() + + api = wandb.Api() + runs = [api.run(rp) for rp in wandb_run_paths] + algo = runs[0].config["algo"] + hyperparam_id = runs[0].config["env"] + evaluations = [ + evaluate_model( + EvalArgs( + algo, + hyperparam_id, + seed=r.config.get("seed", None), + render=False, + best=True, + n_envs=None, + n_episodes=10, + no_print_returns=True, + wandb_run_path="/".join(r.path), + ), + os.path.dirname(__file__), + ) + for r in runs + ] + run_metadata = requests.get(runs[0].file("wandb-metadata.json").url).json() + table_data = list(EvalTableData(r, e) for r, e in zip(runs, evaluations)) + best_eval = sorted( + table_data, key=lambda d: d.evaluation.stats.score, reverse=True + )[0] + + with tempfile.TemporaryDirectory() as tmpdirname: + _, (policy, stats, config) = best_eval + + repo_name = config.model_name(include_seed=False) + repo_dir_path = os.path.join(tmpdirname, repo_name) + # Locally clone this repo to a temp directory + subprocess.run(["git", "clone", ".", repo_dir_path]) + shutil.rmtree(os.path.join(repo_dir_path, ".git")) + model_path = config.model_dir_path(best=True, downloaded=True) + shutil.copytree( + model_path, + os.path.join( + repo_dir_path, "saved_models", config.model_dir_name(best=True) + ), + ) + + github_url = "https://github.com/sgoodfriend/rl-algo-impls" + commit_hash = run_metadata.get("git", {}).get("commit", None) + env_id = runs[0].config.get("env_id") or runs[0].config["env"] + card_text = model_card_text( + algo, + env_id, + github_url, + commit_hash, + wandb_report_url, + table_data, + best_eval, + ) + readme_filepath = os.path.join(repo_dir_path, "README.md") + os.remove(readme_filepath) + with open(readme_filepath, "w") as f: + f.write(card_text) + + metadata = { + "library_name": "rl-algo-impls", + "tags": [ + env_id, + algo, + "deep-reinforcement-learning", + "reinforcement-learning", + ], + "model-index": [ + { + "name": algo, + "results": [ + { + "metrics": [ + { + "type": "mean_reward", + "value": str(stats.score), + "name": "mean_reward", + } + ], + "task": { + "type": "reinforcement-learning", + "name": "reinforcement-learning", + }, + "dataset": { + "name": env_id, + "type": env_id, + }, + } + ], + } + ], + } + metadata_save(readme_filepath, metadata) + + video_env = VecEpisodeRecorder( + make_eval_env( + config, + EnvHyperparams(**config.env_hyperparams), + override_n_envs=1, + normalize_load_path=model_path, + ), + os.path.join(repo_dir_path, "replay"), + max_video_length=3600, + ) + evaluate( + video_env, + policy, + 1, + deterministic=config.eval_params.get("deterministic", True), + ) + + api = HfApi() + huggingface_user = huggingface_user or api.whoami()["name"] + huggingface_repo = f"{huggingface_user}/{repo_name}" + api.create_repo( + token=huggingface_token, + repo_id=huggingface_repo, + private=False, + exist_ok=True, + ) + repo_url = upload_folder( + repo_id=huggingface_repo, + folder_path=repo_dir_path, + path_in_repo="", + commit_message=f"{algo.upper()} playing {env_id} from {github_url}/tree/{commit_hash}", + token=huggingface_token, + ) + print(f"Pushed model to the hub: {repo_url}") + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument( + "--wandb-run-paths", + type=str, + nargs="+", + help="Run paths of the form entity/project/run_id", + ) + parser.add_argument("--wandb-report-url", type=str, help="Link to WandB report") + parser.add_argument( + "--huggingface-user", + type=str, + help="Huggingface user or team to upload model cards", + default=None, + ) + parser.add_argument( + "--virtual-display", action="store_true", help="Use headless virtual display" + ) + args = parser.parse_args() + print(args) + publish(**vars(args)) diff --git a/hyperparams/a2c.yml b/hyperparams/a2c.yml new file mode 100644 index 0000000000000000000000000000000000000000..508c17d3442592e5cdedca7e16e2bf1bd466ee78 --- /dev/null +++ b/hyperparams/a2c.yml @@ -0,0 +1,127 @@ +CartPole-v1: &cartpole-defaults + n_timesteps: !!float 5e5 + env_hyperparams: + n_envs: 8 + +CartPole-v0: + <<: *cartpole-defaults + +MountainCar-v0: + n_timesteps: !!float 1e6 + env_hyperparams: + n_envs: 16 + normalize: true + +MountainCarContinuous-v0: + n_timesteps: !!float 1e5 + env_hyperparams: + n_envs: 4 + normalize: true + # policy_hyperparams: + # use_sde: true + # log_std_init: 0.0 + # init_layers_orthogonal: false + algo_hyperparams: + n_steps: 100 + sde_sample_freq: 16 + +Acrobot-v1: + n_timesteps: !!float 5e5 + env_hyperparams: + normalize: true + n_envs: 16 + +LunarLander-v2: + n_timesteps: !!float 1e6 + env_hyperparams: + n_envs: 8 + normalize: true + algo_hyperparams: + n_steps: 5 + gamma: 0.995 + learning_rate: !!float 8.3e-4 + learning_rate_decay: linear + ent_coef: !!float 1e-5 + +BipedalWalker-v3: + n_timesteps: !!float 5e6 + env_hyperparams: + n_envs: 16 + normalize: true + policy_hyperparams: + use_sde: true + log_std_init: -2 + init_layers_orthogonal: false + algo_hyperparams: + ent_coef: 0 + max_grad_norm: 0.5 + n_steps: 8 + gae_lambda: 0.9 + vf_coef: 0.4 + gamma: 0.99 + learning_rate: !!float 9.6e-4 + learning_rate_decay: linear + +HalfCheetahBulletEnv-v0: &pybullet-defaults + n_timesteps: !!float 2e6 + env_hyperparams: + n_envs: 4 + normalize: true + policy_hyperparams: + use_sde: true + log_std_init: -2 + init_layers_orthogonal: false + algo_hyperaparms: &pybullet-algo-defaults + n_steps: 8 + ent_coef: 0 + max_grad_norm: 0.5 + gae_lambda: 0.9 + gamma: 0.99 + vf_coef: 0.4 + learning_rate: !!float 9.6e-4 + learning_rate_decay: linear + +AntBulletEnv-v0: + <<: *pybullet-defaults + +Walker2DBulletEnv-v0: + <<: *pybullet-defaults + +HopperBulletEnv-v0: + <<: *pybullet-defaults + +CarRacing-v0: + n_timesteps: !!float 4e6 + env_hyperparams: + n_envs: 8 + frame_stack: 4 + policy_hyperparams: + use_sde: true + log_std_init: -2 + init_layers_orthogonal: false + activation_fn: relu + share_features_extractor: false + cnn_feature_dim: 256 + hidden_sizes: [256] + algo_hyperparams: + n_steps: 8 + learning_rate: !!float 5e-5 + learning_rate_decay: linear + gamma: 0.99 + gae_lambda: 0.95 + ent_coef: 0 + sde_sample_freq: 4 + +_atari: &atari-defaults + n_timesteps: !!float 1e7 + env_hyperparams: &atari-env-defaults + n_envs: 16 + frame_stack: 4 + no_reward_timeout_steps: 1000 + no_reward_fire_steps: 500 + vec_env_class: async + policy_hyperparams: &atari-policy-defaults + activation_fn: relu + algo_hyperparams: + ent_coef: 0.01 + vf_coef: 0.25 diff --git a/hyperparams/dqn.yml b/hyperparams/dqn.yml new file mode 100644 index 0000000000000000000000000000000000000000..66003a67dd8c7865fd9ea269f6fc84b5d95fb428 --- /dev/null +++ b/hyperparams/dqn.yml @@ -0,0 +1,130 @@ +CartPole-v1: &cartpole-defaults + n_timesteps: !!float 5e4 + env_hyperparams: + rolling_length: 50 + policy_hyperparams: + hidden_sizes: [256, 256] + algo_hyperparams: + learning_rate: !!float 2.3e-3 + batch_size: 64 + buffer_size: 100000 + learning_starts: 1000 + gamma: 0.99 + target_update_interval: 10 + train_freq: 256 + gradient_steps: 128 + exploration_fraction: 0.16 + exploration_final_eps: 0.04 + eval_params: + step_freq: !!float 1e4 + +CartPole-v0: + <<: *cartpole-defaults + n_timesteps: !!float 4e4 + +MountainCar-v0: + n_timesteps: !!float 1.2e5 + env_hyperparams: + rolling_length: 50 + policy_hyperparams: + hidden_sizes: [256, 256] + algo_hyperparams: + learning_rate: !!float 4e-3 + batch_size: 128 + buffer_size: 10000 + learning_starts: 1000 + gamma: 0.98 + target_update_interval: 600 + train_freq: 16 + gradient_steps: 8 + exploration_fraction: 0.2 + exploration_final_eps: 0.07 + +Acrobot-v1: + n_timesteps: !!float 1e5 + env_hyperparams: + rolling_length: 50 + policy_hyperparams: + hidden_sizes: [256, 256] + algo_hyperparams: + learning_rate: !!float 6.3e-4 + batch_size: 128 + buffer_size: 50000 + learning_starts: 0 + gamma: 0.99 + target_update_interval: 250 + train_freq: 4 + gradient_steps: -1 + exploration_fraction: 0.12 + exploration_final_eps: 0.1 + +LunarLander-v2: + n_timesteps: !!float 5e5 + env_hyperparams: + rolling_length: 50 + policy_hyperparams: + hidden_sizes: [256, 256] + algo_hyperparams: + learning_rate: !!float 1e-4 + batch_size: 256 + buffer_size: 100000 + learning_starts: 10000 + gamma: 0.99 + target_update_interval: 250 + train_freq: 8 + gradient_steps: -1 + exploration_fraction: 0.12 + exploration_final_eps: 0.1 + max_grad_norm: 0.5 + eval_params: + step_freq: 25_000 + +_atari: &atari-defaults + n_timesteps: !!float 1e7 + env_hyperparams: + frame_stack: 4 + no_reward_timeout_steps: 1_000 + no_reward_fire_steps: 500 + n_envs: 8 + vec_env_class: async + algo_hyperparams: + buffer_size: 100000 + learning_rate: !!float 1e-4 + batch_size: 32 + learning_starts: 100000 + target_update_interval: 1000 + train_freq: 8 + gradient_steps: 2 + exploration_fraction: 0.1 + exploration_final_eps: 0.01 + eval_params: + deterministic: false + +PongNoFrameskip-v4: + <<: *atari-defaults + n_timesteps: !!float 2.5e6 + +_impala-atari: &impala-atari-defaults + <<: *atari-defaults + policy_hyperparams: + cnn_style: impala + cnn_feature_dim: 256 + init_layers_orthogonal: true + cnn_layers_init_orthogonal: false + +impala-PongNoFrameskip-v4: + <<: *impala-atari-defaults + env_id: PongNoFrameskip-v4 + n_timesteps: !!float 2.5e6 + +impala-BreakoutNoFrameskip-v4: + <<: *impala-atari-defaults + env_id: BreakoutNoFrameskip-v4 + +impala-SpaceInvadersNoFrameskip-v4: + <<: *impala-atari-defaults + env_id: SpaceInvadersNoFrameskip-v4 + +impala-QbertNoFrameskip-v4: + <<: *impala-atari-defaults + env_id: QbertNoFrameskip-v4 diff --git a/hyperparams/ppo.yml b/hyperparams/ppo.yml new file mode 100644 index 0000000000000000000000000000000000000000..0136fc46b019f9722aaad54bfe93f36ed88f4bc8 --- /dev/null +++ b/hyperparams/ppo.yml @@ -0,0 +1,383 @@ +CartPole-v1: &cartpole-defaults + n_timesteps: !!float 1e5 + env_hyperparams: + n_envs: 8 + algo_hyperparams: + n_steps: 32 + batch_size: 256 + n_epochs: 20 + gae_lambda: 0.8 + gamma: 0.98 + ent_coef: 0.0 + learning_rate: 0.001 + learning_rate_decay: linear + clip_range: 0.2 + clip_range_decay: linear + eval_params: + step_freq: !!float 2.5e4 + +CartPole-v0: + <<: *cartpole-defaults + n_timesteps: !!float 5e4 + +MountainCar-v0: + n_timesteps: !!float 1e6 + env_hyperparams: + normalize: true + n_envs: 16 + algo_hyperparams: + n_steps: 16 + n_epochs: 4 + gae_lambda: 0.98 + gamma: 0.99 + ent_coef: 0.0 + +MountainCarContinuous-v0: + n_timesteps: !!float 1e5 + env_hyperparams: + normalize: true + n_envs: 4 + # policy_hyperparams: + # init_layers_orthogonal: false + # log_std_init: -3.29 + # use_sde: true + algo_hyperparams: + n_steps: 512 + batch_size: 256 + n_epochs: 10 + learning_rate: !!float 7.77e-5 + ent_coef: 0.01 # 0.00429 + ent_coef_decay: linear + clip_range: 0.1 + gae_lambda: 0.9 + max_grad_norm: 5 + vf_coef: 0.19 + eval_params: + step_freq: 5000 + +Acrobot-v1: + n_timesteps: !!float 1e6 + env_hyperparams: + n_envs: 16 + normalize: true + algo_hyperparams: + n_steps: 256 + n_epochs: 4 + gae_lambda: 0.94 + gamma: 0.99 + ent_coef: 0.0 + +LunarLander-v2: + n_timesteps: !!float 4e6 + env_hyperparams: + n_envs: 16 + algo_hyperparams: + n_steps: 1024 + batch_size: 64 + n_epochs: 4 + gae_lambda: 0.98 + gamma: 0.999 + learning_rate: !!float 5e-4 + learning_rate_decay: linear + clip_range: 0.2 + clip_range_decay: linear + ent_coef: 0.01 + normalize_advantage: false + +BipedalWalker-v3: + n_timesteps: !!float 10e6 + env_hyperparams: + n_envs: 16 + normalize: true + algo_hyperparams: + n_steps: 2048 + batch_size: 64 + gae_lambda: 0.95 + gamma: 0.99 + n_epochs: 10 + ent_coef: 0.001 + learning_rate: !!float 2.5e-4 + learning_rate_decay: linear + clip_range: 0.2 + clip_range_decay: linear + +CarRacing-v0: &carracing-defaults + n_timesteps: !!float 4e6 + env_hyperparams: + n_envs: 8 + frame_stack: 4 + policy_hyperparams: &carracing-policy-defaults + use_sde: true + log_std_init: -2 + init_layers_orthogonal: false + activation_fn: relu + share_features_extractor: false + cnn_feature_dim: 256 + hidden_sizes: [256] + algo_hyperparams: + n_steps: 512 + batch_size: 128 + n_epochs: 10 + learning_rate: !!float 1e-4 + learning_rate_decay: linear + gamma: 0.99 + gae_lambda: 0.95 + ent_coef: 0.0 + sde_sample_freq: 4 + max_grad_norm: 0.5 + vf_coef: 0.5 + clip_range: 0.2 + +impala-CarRacing-v0: + <<: *carracing-defaults + env_id: CarRacing-v0 + policy_hyperparams: + <<: *carracing-policy-defaults + cnn_style: impala + init_layers_orthogonal: true + cnn_layers_init_orthogonal: false + hidden_sizes: [] + +# BreakoutNoFrameskip-v4 +# PongNoFrameskip-v4 +# SpaceInvadersNoFrameskip-v4 +# QbertNoFrameskip-v4 +_atari: &atari-defaults + n_timesteps: !!float 1e7 + env_hyperparams: &atari-env-defaults + n_envs: 8 + frame_stack: 4 + no_reward_timeout_steps: 1000 + no_reward_fire_steps: 500 + vec_env_class: async + policy_hyperparams: &atari-policy-defaults + activation_fn: relu + algo_hyperparams: + n_steps: 128 + batch_size: 256 + n_epochs: 4 + learning_rate: !!float 2.5e-4 + learning_rate_decay: linear + clip_range: 0.1 + clip_range_decay: linear + vf_coef: 0.5 + ent_coef: 0.01 + eval_params: + deterministic: false + +_norm-rewards-atari: &norm-rewards-atari-default + <<: *atari-defaults + env_hyperparams: + <<: *atari-env-defaults + clip_atari_rewards: false + normalize: true + normalize_kwargs: + norm_obs: false + norm_reward: true + +norm-rewards-BreakoutNoFrameskip-v4: + <<: *norm-rewards-atari-default + env_id: BreakoutNoFrameskip-v4 + +debug-PongNoFrameskip-v4: + <<: *atari-defaults + device: cpu + env_id: PongNoFrameskip-v4 + env_hyperparams: + <<: *atari-env-defaults + vec_env_class: sync + +_impala-atari: &impala-atari-defaults + <<: *atari-defaults + policy_hyperparams: + <<: *atari-policy-defaults + cnn_style: impala + cnn_feature_dim: 256 + init_layers_orthogonal: true + cnn_layers_init_orthogonal: false + +impala-PongNoFrameskip-v4: + <<: *impala-atari-defaults + env_id: PongNoFrameskip-v4 + +impala-BreakoutNoFrameskip-v4: + <<: *impala-atari-defaults + env_id: BreakoutNoFrameskip-v4 + +impala-SpaceInvadersNoFrameskip-v4: + <<: *impala-atari-defaults + env_id: SpaceInvadersNoFrameskip-v4 + +impala-QbertNoFrameskip-v4: + <<: *impala-atari-defaults + env_id: QbertNoFrameskip-v4 + +HalfCheetahBulletEnv-v0: &pybullet-defaults + n_timesteps: !!float 2e6 + env_hyperparams: &pybullet-env-defaults + n_envs: 16 + normalize: true + policy_hyperparams: &pybullet-policy-defaults + pi_hidden_sizes: [256, 256] + v_hidden_sizes: [256, 256] + activation_fn: relu + algo_hyperparams: &pybullet-algo-defaults + n_steps: 512 + batch_size: 128 + n_epochs: 20 + gamma: 0.99 + gae_lambda: 0.9 + ent_coef: 0.0 + max_grad_norm: 0.5 + vf_coef: 0.5 + learning_rate: !!float 3e-5 + clip_range: 0.4 + +AntBulletEnv-v0: + <<: *pybullet-defaults + policy_hyperparams: + <<: *pybullet-policy-defaults + algo_hyperparams: + <<: *pybullet-algo-defaults + +Walker2DBulletEnv-v0: + <<: *pybullet-defaults + algo_hyperparams: + <<: *pybullet-algo-defaults + clip_range_decay: linear + +HopperBulletEnv-v0: + <<: *pybullet-defaults + algo_hyperparams: + <<: *pybullet-algo-defaults + clip_range_decay: linear + +HumanoidBulletEnv-v0: + <<: *pybullet-defaults + n_timesteps: !!float 1e7 + env_hyperparams: + <<: *pybullet-env-defaults + n_envs: 8 + policy_hyperparams: + <<: *pybullet-policy-defaults + # log_std_init: -1 + algo_hyperparams: + <<: *pybullet-algo-defaults + n_steps: 2048 + batch_size: 64 + n_epochs: 10 + gae_lambda: 0.95 + learning_rate: !!float 2.5e-4 + clip_range: 0.2 + +_procgen: &procgen-defaults + env_hyperparams: &procgen-env-defaults + env_type: procgen + n_envs: 64 + # grayscale: false + # frame_stack: 4 + normalize: true # procgen only normalizes reward + make_kwargs: &procgen-make-kwargs-defaults + num_threads: 8 + policy_hyperparams: &procgen-policy-defaults + activation_fn: relu + cnn_style: impala + cnn_feature_dim: 256 + init_layers_orthogonal: true + cnn_layers_init_orthogonal: false + algo_hyperparams: &procgen-algo-defaults + gamma: 0.999 + gae_lambda: 0.95 + n_steps: 256 + batch_size: 2048 + n_epochs: 3 + ent_coef: 0.01 + clip_range: 0.2 + # clip_range_decay: linear + clip_range_vf: 0.2 + learning_rate: !!float 5e-4 + # learning_rate_decay: linear + vf_coef: 0.5 + eval_params: &procgen-eval-defaults + ignore_first_episode: true + # deterministic: false + step_freq: !!float 1e5 + +_procgen-easy: &procgen-easy-defaults + <<: *procgen-defaults + n_timesteps: !!float 25e6 + env_hyperparams: &procgen-easy-env-defaults + <<: *procgen-env-defaults + make_kwargs: + <<: *procgen-make-kwargs-defaults + distribution_mode: easy + +procgen-coinrun-easy: &coinrun-easy-defaults + <<: *procgen-easy-defaults + env_id: coinrun + +debug-procgen-coinrun: + <<: *coinrun-easy-defaults + device: cpu + +procgen-starpilot-easy: + <<: *procgen-easy-defaults + env_id: starpilot + +procgen-bossfight-easy: + <<: *procgen-easy-defaults + env_id: bossfight + +procgen-bigfish-easy: + <<: *procgen-easy-defaults + env_id: bigfish + +_procgen-hard: &procgen-hard-defaults + <<: *procgen-defaults + n_timesteps: !!float 200e6 + env_hyperparams: &procgen-hard-env-defaults + <<: *procgen-env-defaults + n_envs: 256 + make_kwargs: + <<: *procgen-make-kwargs-defaults + distribution_mode: hard + algo_hyperparams: &procgen-hard-algo-defaults + <<: *procgen-algo-defaults + batch_size: 8192 + clip_range_decay: linear + learning_rate_decay: linear + eval_params: + <<: *procgen-eval-defaults + step_freq: !!float 5e5 + +procgen-starpilot-hard: &procgen-starpilot-hard-defaults + <<: *procgen-hard-defaults + env_id: starpilot + +procgen-starpilot-hard-2xIMPALA: + <<: *procgen-starpilot-hard-defaults + policy_hyperparams: + <<: *procgen-policy-defaults + impala_channels: [32, 64, 64] + algo_hyperparams: + <<: *procgen-hard-algo-defaults + learning_rate: !!float 3.3e-4 + +procgen-starpilot-hard-2xIMPALA-fat: + <<: *procgen-starpilot-hard-defaults + policy_hyperparams: + <<: *procgen-policy-defaults + impala_channels: [32, 64, 64] + cnn_feature_dim: 512 + algo_hyperparams: + <<: *procgen-hard-algo-defaults + learning_rate: !!float 2.5e-4 + +procgen-starpilot-hard-4xIMPALA: + <<: *procgen-starpilot-hard-defaults + policy_hyperparams: + <<: *procgen-policy-defaults + impala_channels: [64, 128, 128] + algo_hyperparams: + <<: *procgen-hard-algo-defaults + learning_rate: !!float 2.1e-4 diff --git a/hyperparams/vpg.yml b/hyperparams/vpg.yml new file mode 100644 index 0000000000000000000000000000000000000000..e472a9226b830c127f044718672d6d0c9e8c83dc --- /dev/null +++ b/hyperparams/vpg.yml @@ -0,0 +1,197 @@ +CartPole-v1: &cartpole-defaults + n_timesteps: !!float 4e5 + algo_hyperparams: + n_steps: 4096 + pi_lr: 0.01 + gamma: 0.99 + gae_lambda: 1 + val_lr: 0.01 + train_v_iters: 80 + eval_params: + step_freq: !!float 2.5e4 + +CartPole-v0: + <<: *cartpole-defaults + n_timesteps: !!float 1e5 + algo_hyperparams: + n_steps: 1024 + pi_lr: 0.01 + gamma: 0.99 + gae_lambda: 1 + val_lr: 0.01 + train_v_iters: 80 + +MountainCar-v0: + n_timesteps: !!float 1e6 + env_hyperparams: + normalize: true + n_envs: 16 + algo_hyperparams: + n_steps: 200 + pi_lr: 0.005 + gamma: 0.99 + gae_lambda: 0.97 + val_lr: 0.01 + train_v_iters: 80 + max_grad_norm: 0.5 + +MountainCarContinuous-v0: + n_timesteps: !!float 3e5 + env_hyperparams: + normalize: true + n_envs: 4 + # policy_hyperparams: + # init_layers_orthogonal: false + # log_std_init: -3.29 + # use_sde: true + algo_hyperparams: + n_steps: 1000 + pi_lr: !!float 5e-4 + gamma: 0.99 + gae_lambda: 0.9 + val_lr: !!float 1e-3 + train_v_iters: 80 + max_grad_norm: 5 + eval_params: + step_freq: 5000 + +Acrobot-v1: + n_timesteps: !!float 2e5 + algo_hyperparams: + n_steps: 2048 + pi_lr: 0.005 + gamma: 0.99 + gae_lambda: 0.97 + val_lr: 0.01 + train_v_iters: 80 + max_grad_norm: 0.5 + +LunarLander-v2: + n_timesteps: !!float 4e6 + policy_hyperparams: + hidden_sizes: [256, 256] + algo_hyperparams: + n_steps: 2048 + pi_lr: 0.0001 + gamma: 0.999 + gae_lambda: 0.97 + val_lr: 0.0001 + train_v_iters: 80 + max_grad_norm: 0.5 + eval_params: + deterministic: false + +BipedalWalker-v3: + n_timesteps: !!float 10e6 + env_hyperparams: + n_envs: 16 + normalize: true + policy_hyperparams: + hidden_sizes: [256, 256] + algo_hyperparams: + n_steps: 1600 + gae_lambda: 0.95 + gamma: 0.99 + pi_lr: !!float 1e-4 + val_lr: !!float 1e-4 + train_v_iters: 80 + max_grad_norm: 0.5 + eval_params: + deterministic: false + +CarRacing-v0: + n_timesteps: !!float 4e6 + env_hyperparams: + frame_stack: 4 + n_envs: 4 + vec_env_class: sync + policy_hyperparams: + use_sde: true + log_std_init: -2 + init_layers_orthogonal: false + activation_fn: relu + cnn_feature_dim: 256 + hidden_sizes: [256] + algo_hyperparams: + n_steps: 1000 + pi_lr: !!float 5e-5 + gamma: 0.99 + gae_lambda: 0.95 + val_lr: !!float 1e-4 + train_v_iters: 40 + max_grad_norm: 0.5 + sde_sample_freq: 4 + +HalfCheetahBulletEnv-v0: &pybullet-defaults + n_timesteps: !!float 2e6 + env_hyperparams: &pybullet-env-defaults + normalize: true + policy_hyperparams: &pybullet-policy-defaults + hidden_sizes: [256, 256] + algo_hyperparams: &pybullet-algo-defaults + n_steps: 4000 + pi_lr: !!float 3e-4 + gamma: 0.99 + gae_lambda: 0.97 + val_lr: !!float 1e-3 + train_v_iters: 80 + max_grad_norm: 0.5 + +AntBulletEnv-v0: + <<: *pybullet-defaults + policy_hyperparams: + <<: *pybullet-policy-defaults + hidden_sizes: [400, 300] + algo_hyperparams: + <<: *pybullet-algo-defaults + pi_lr: !!float 7e-4 + val_lr: !!float 7e-3 + +HopperBulletEnv-v0: + <<: *pybullet-defaults + +Walker2DBulletEnv-v0: + <<: *pybullet-defaults + +FrozenLake-v1: + n_timesteps: !!float 8e5 + env_params: + make_kwargs: + map_name: 8x8 + is_slippery: true + policy_hyperparams: + hidden_sizes: [64] + algo_hyperparams: + n_steps: 2048 + pi_lr: 0.01 + gamma: 0.99 + gae_lambda: 0.98 + val_lr: 0.01 + train_v_iters: 80 + max_grad_norm: 0.5 + eval_params: + step_freq: !!float 5e4 + n_episodes: 10 + save_best: true + +_atari: &atari-defaults + n_timesteps: !!float 25e6 + env_hyperparams: + n_envs: 4 + frame_stack: 4 + no_reward_timeout_steps: 1000 + no_reward_fire_steps: 500 + vec_env_class: async + policy_hyperparams: + activation_fn: relu + algo_hyperparams: + n_steps: 2048 + pi_lr: !!float 5e-5 + gamma: 0.99 + gae_lambda: 0.95 + val_lr: !!float 1e-4 + train_v_iters: 80 + max_grad_norm: 0.5 + ent_coef: 0.01 + eval_params: + deterministic: false diff --git a/lambda_labs/benchmark.sh b/lambda_labs/benchmark.sh new file mode 100644 index 0000000000000000000000000000000000000000..624abe32380f7dfd37cffb1cf2693980a7eeeddd --- /dev/null +++ b/lambda_labs/benchmark.sh @@ -0,0 +1,34 @@ +source benchmarks/train_loop.sh + +# export WANDB_PROJECT_NAME="rl-algo-impls" + +BENCHMARK_MAX_PROCS="${BENCHMARK_MAX_PROCS:-6}" + +ALGOS=( + # "vpg" + # "dqn" + # "ppo" + "a2c" +) +ENVS=( + # Basic + "CartPole-v1" + "MountainCar-v0" + "MountainCarContinuous-v0" + "Acrobot-v1" + "LunarLander-v2" + "BipedalWalker-v3" + # PyBullet + "HalfCheetahBulletEnv-v0" + "AntBulletEnv-v0" + "HopperBulletEnv-v0" + "Walker2DBulletEnv-v0" + # CarRacing + "CarRacing-v0" + # Atari + "PongNoFrameskip-v4" + "BreakoutNoFrameskip-v4" + "SpaceInvadersNoFrameskip-v4" + "QbertNoFrameskip-v4" +) +train_loop "${ALGOS[*]}" "${ENVS[*]}" | xargs -I CMD -P $BENCHMARK_MAX_PROCS bash -c CMD diff --git a/lambda_labs/impala_atari_benchmark.sh b/lambda_labs/impala_atari_benchmark.sh new file mode 100644 index 0000000000000000000000000000000000000000..a9ae78022ef1ae1995ec14495fdd8af287e30e89 --- /dev/null +++ b/lambda_labs/impala_atari_benchmark.sh @@ -0,0 +1,19 @@ +source benchmarks/train_loop.sh + +# export WANDB_PROJECT_NAME="rl-algo-impls" + +BENCHMARK_MAX_PROCS="${BENCHMARK_MAX_PROCS:-5}" + +ALGOS=( + # "vpg" + # "dqn" + "ppo" +) +ENVS=( + "impala-PongNoFrameskip-v4" + "impala-BreakoutNoFrameskip-v4" + "impala-SpaceInvadersNoFrameskip-v4" + "impala-QbertNoFrameskip-v4" + "impala-CarRacing-v0" +) +train_loop "${ALGOS[*]}" "${ENVS[*]}" | xargs -I CMD -P $BENCHMARK_MAX_PROCS bash -c CMD diff --git a/lambda_labs/lambda_requirements.txt b/lambda_labs/lambda_requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..62e1bc45d79e95d758bc92915d99ee612092f56e --- /dev/null +++ b/lambda_labs/lambda_requirements.txt @@ -0,0 +1,16 @@ +scipy >= 1.10.0, < 1.11 +tensorboard >= ^2.11.0, < 2.12 +AutoROM.accept-rom-license >= 0.4.2, < 0.5 +stable-baselines3[extra] >= 1.7.0, < 1.8 +gym[box2d] >= 0.21.0, < 0.22 +pyglet == 1.5.27 +wandb >= 0.13.10, < 0.14 +pyvirtualdisplay == 3.0 +pybullet >= 3.2.5, < 3.3 +tabulate >= 0.9.0, < 0.10 +huggingface-hub >= 0.12.0, < 0.13 +numexpr >= 2.8.4, < 2.9 +gym3 >= 0.3.3, < 0.4 +glfw >= 1.12.0, < 1.13 +procgen >= 0.10.7, < 0.11 +ipython >= 8.10.0, < 8.11 \ No newline at end of file diff --git a/lambda_labs/procgen_benchmark.sh b/lambda_labs/procgen_benchmark.sh new file mode 100644 index 0000000000000000000000000000000000000000..9e58f69ed4fcf6bbc58cca335ce3badbd9e3ca58 --- /dev/null +++ b/lambda_labs/procgen_benchmark.sh @@ -0,0 +1,18 @@ +source benchmarks/train_loop.sh + +# export WANDB_PROJECT_NAME="rl-algo-impls" + +BENCHMARK_MAX_PROCS="${BENCHMARK_MAX_PROCS:-3}" + +ALGOS=( + # "vpg" + # "dqn" + "ppo" +) +ENVS=( + "procgen-coinrun-easy" + "procgen-starpilot-easy" + "procgen-bossfight-easy" + "procgen-bigfish-easy" +) +train_loop "${ALGOS[*]}" "${ENVS[*]}" | xargs -I CMD -P $BENCHMARK_MAX_PROCS bash -c CMD diff --git a/lambda_labs/setup.sh b/lambda_labs/setup.sh new file mode 100644 index 0000000000000000000000000000000000000000..79d7f0a0b9bcc087b3b40065830bc8afef29ec18 --- /dev/null +++ b/lambda_labs/setup.sh @@ -0,0 +1,10 @@ +sudo apt update +sudo apt install -y python-opengl +sudo apt install -y ffmpeg +sudo apt install -y xvfb +sudo apt install -y swig + +python3 -m pip install --upgrade pip +pip install --upgrade torch torchvision torchaudio + +pip install --upgrade -r ~/rl-algo-impls/lambda_labs/lambda_requirements.txt \ No newline at end of file diff --git a/lambda_labs/starpilot_hard_benchmark.sh b/lambda_labs/starpilot_hard_benchmark.sh new file mode 100644 index 0000000000000000000000000000000000000000..da6897b33e0f85917288ba743ba6eb06d20e2176 --- /dev/null +++ b/lambda_labs/starpilot_hard_benchmark.sh @@ -0,0 +1,16 @@ +source benchmarks/train_loop.sh + +# export WANDB_PROJECT_NAME="rl-algo-impls" + +BENCHMARK_MAX_PROCS="${BENCHMARK_MAX_PROCS:-1}" + +ALGOS=( + "ppo" +) +ENVS=( + "procgen-starpilot-hard" + "procgen-starpilot-hard-2xIMPALA" + "procgen-starpilot-hard-2xIMPALA-fat" + "procgen-starpilot-hard-4xIMPALA" +) +train_loop "${ALGOS[*]}" "${ENVS[*]}" | xargs -I CMD -P $BENCHMARK_MAX_PROCS bash -c CMD diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000000000000000000000000000000000000..da115fe5e6e3297f30cd68c4a85ca151fe1414aa --- /dev/null +++ b/poetry.lock @@ -0,0 +1,4466 @@ +# This file is automatically @generated by Poetry and should not be changed by hand. + +[[package]] +name = "absl-py" +version = "1.4.0" +description = "Abseil Python Common Libraries, see https://github.com/abseil/abseil-py." +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "absl-py-1.4.0.tar.gz", hash = "sha256:d2c244d01048ba476e7c080bd2c6df5e141d211de80223460d5b3b8a2a58433d"}, + {file = "absl_py-1.4.0-py3-none-any.whl", hash = "sha256:0d3fe606adfa4f7db64792dd4c7aee4ee0c38ab75dfd353b7a83ed3e957fcb47"}, +] + +[[package]] +name = "ale-py" +version = "0.7.4" +description = "The Arcade Learning Environment (ALE) - a platform for AI research." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "ale_py-0.7.4-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:418eea1539c2669c799274fedead4d44d05dfc3dcd6c536378d5984c42bc340b"}, + {file = "ale_py-0.7.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:38e4823be04761a2ebc0167ed710a318cc9f0fec3815576c45030fe8e67f9c98"}, + {file = "ale_py-0.7.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9af49488ec1b4facb299975a665e9e706921dd2d756daad813e2897debc5fc3c"}, + {file = "ale_py-0.7.4-cp310-cp310-win_amd64.whl", hash = "sha256:f600c55d6a7c6c30f5592b30afc34366101fc7561842bdd5740d5bca390201eb"}, + {file = "ale_py-0.7.4-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:da3e1400e02fb46659dfb3af92e8a4cf4c5b2d4f9d19a008ce9d5fa8eebb4ab6"}, + {file = "ale_py-0.7.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c073005b68901f0003ffe871d56021245eda9e88f27cc91745627c099932499f"}, + {file = "ale_py-0.7.4-cp37-cp37m-win_amd64.whl", hash = "sha256:913394ad1dbe22a8d489378d702f296234721ca0a0e76e5354764e8bf40bc623"}, + {file = "ale_py-0.7.4-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:4841f395e3166d4a7b1e9207cafab08de4b9e9b4178afd97a36f53844ade98a2"}, + {file = "ale_py-0.7.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5b2899b4cf659bc14a20047455e681e991cb96ceed937d22a5dac1a97a16bf3e"}, + {file = "ale_py-0.7.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9aff7a8ce37d00a87ef4114666db0b45d499744d08f5ff1683dbbbcac4783569"}, + {file = "ale_py-0.7.4-cp38-cp38-win_amd64.whl", hash = "sha256:a23f4c858a2c5cbfa3c0cb2c9ab167359c368104b67e19b332710c19b43c6091"}, + {file = "ale_py-0.7.4-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:0b9ab62f12a325e92ba2af99c5b231ad3b219a46913b14068c857d37837025fb"}, + {file = "ale_py-0.7.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:269dcf94024ba7a8276d4dcf04c526df695cb383aa2372e9903a08ec6f679262"}, + {file = "ale_py-0.7.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f65371c180779b115d8600d99780e9e83b229812e94c6b49be1686ce4d82573"}, + {file = "ale_py-0.7.4-cp39-cp39-win_amd64.whl", hash = "sha256:b53e7d0c8f8e8610ebaec88887da2427ce16811f9697ccbb39588ec784bea145"}, +] + +[package.dependencies] +importlib-resources = "*" +numpy = "*" + +[package.extras] +test = ["gym", "pytest"] + +[[package]] +name = "anyio" +version = "3.6.2" +description = "High level compatibility layer for multiple asynchronous event loop implementations" +category = "main" +optional = false +python-versions = ">=3.6.2" +files = [ + {file = "anyio-3.6.2-py3-none-any.whl", hash = "sha256:fbbe32bd270d2a2ef3ed1c5d45041250284e31fc0a4df4a5a6071842051a51e3"}, + {file = "anyio-3.6.2.tar.gz", hash = "sha256:25ea0d673ae30af41a0c442f81cf3b38c7e79fdc7b60335a4c14e05eb0947421"}, +] + +[package.dependencies] +idna = ">=2.8" +sniffio = ">=1.1" + +[package.extras] +doc = ["packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] +test = ["contextlib2", "coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "mock (>=4)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (<0.15)", "uvloop (>=0.15)"] +trio = ["trio (>=0.16,<0.22)"] + +[[package]] +name = "appdirs" +version = "1.4.4" +description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"}, + {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, +] + +[[package]] +name = "appnope" +version = "0.1.3" +description = "Disable App Nap on macOS >= 10.9" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "appnope-0.1.3-py2.py3-none-any.whl", hash = "sha256:265a455292d0bd8a72453494fa24df5a11eb18373a60c7c0430889f22548605e"}, + {file = "appnope-0.1.3.tar.gz", hash = "sha256:02bd91c4de869fbb1e1c50aafc4098827a7a54ab2f39d9dcba6c9547ed920e24"}, +] + +[[package]] +name = "argon2-cffi" +version = "21.3.0" +description = "The secure Argon2 password hashing algorithm." +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "argon2-cffi-21.3.0.tar.gz", hash = "sha256:d384164d944190a7dd7ef22c6aa3ff197da12962bd04b17f64d4e93d934dba5b"}, + {file = "argon2_cffi-21.3.0-py3-none-any.whl", hash = "sha256:8c976986f2c5c0e5000919e6de187906cfd81fb1c72bf9d88c01177e77da7f80"}, +] + +[package.dependencies] +argon2-cffi-bindings = "*" + +[package.extras] +dev = ["cogapp", "coverage[toml] (>=5.0.2)", "furo", "hypothesis", "pre-commit", "pytest", "sphinx", "sphinx-notfound-page", "tomli"] +docs = ["furo", "sphinx", "sphinx-notfound-page"] +tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pytest"] + +[[package]] +name = "argon2-cffi-bindings" +version = "21.2.0" +description = "Low-level CFFI bindings for Argon2" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "argon2-cffi-bindings-21.2.0.tar.gz", hash = "sha256:bb89ceffa6c791807d1305ceb77dbfacc5aa499891d2c55661c6459651fc39e3"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ccb949252cb2ab3a08c02024acb77cfb179492d5701c7cbdbfd776124d4d2367"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9524464572e12979364b7d600abf96181d3541da11e23ddf565a32e70bd4dc0d"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b746dba803a79238e925d9046a63aa26bf86ab2a2fe74ce6b009a1c3f5c8f2ae"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58ed19212051f49a523abb1dbe954337dc82d947fb6e5a0da60f7c8471a8476c"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:bd46088725ef7f58b5a1ef7ca06647ebaf0eb4baff7d1d0d177c6cc8744abd86"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_i686.whl", hash = "sha256:8cd69c07dd875537a824deec19f978e0f2078fdda07fd5c42ac29668dda5f40f"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:f1152ac548bd5b8bcecfb0b0371f082037e47128653df2e8ba6e914d384f3c3e"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-win32.whl", hash = "sha256:603ca0aba86b1349b147cab91ae970c63118a0f30444d4bc80355937c950c082"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-win_amd64.whl", hash = "sha256:b2ef1c30440dbbcba7a5dc3e319408b59676e2e039e2ae11a8775ecf482b192f"}, + {file = "argon2_cffi_bindings-21.2.0-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:e415e3f62c8d124ee16018e491a009937f8cf7ebf5eb430ffc5de21b900dad93"}, + {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3e385d1c39c520c08b53d63300c3ecc28622f076f4c2b0e6d7e796e9f6502194"}, + {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c3e3cc67fdb7d82c4718f19b4e7a87123caf8a93fde7e23cf66ac0337d3cb3f"}, + {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6a22ad9800121b71099d0fb0a65323810a15f2e292f2ba450810a7316e128ee5"}, + {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f9f8b450ed0547e3d473fdc8612083fd08dd2120d6ac8f73828df9b7d45bb351"}, + {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:93f9bf70084f97245ba10ee36575f0c3f1e7d7724d67d8e5b08e61787c320ed7"}, + {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3b9ef65804859d335dc6b31582cad2c5166f0c3e7975f324d9ffaa34ee7e6583"}, + {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4966ef5848d820776f5f562a7d45fdd70c2f330c961d0d745b784034bd9f48d"}, + {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20ef543a89dee4db46a1a6e206cd015360e5a75822f76df533845c3cbaf72670"}, + {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed2937d286e2ad0cc79a7087d3c272832865f779430e0cc2b4f3718d3159b0cb"}, + {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:5e00316dabdaea0b2dd82d141cc66889ced0cdcbfa599e8b471cf22c620c329a"}, +] + +[package.dependencies] +cffi = ">=1.0.1" + +[package.extras] +dev = ["cogapp", "pre-commit", "pytest", "wheel"] +tests = ["pytest"] + +[[package]] +name = "arrow" +version = "1.2.3" +description = "Better dates & times for Python" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "arrow-1.2.3-py3-none-any.whl", hash = "sha256:5a49ab92e3b7b71d96cd6bfcc4df14efefc9dfa96ea19045815914a6ab6b1fe2"}, + {file = "arrow-1.2.3.tar.gz", hash = "sha256:3934b30ca1b9f292376d9db15b19446088d12ec58629bc3f0da28fd55fb633a1"}, +] + +[package.dependencies] +python-dateutil = ">=2.7.0" + +[[package]] +name = "asttokens" +version = "2.2.1" +description = "Annotate AST trees with source code positions" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "asttokens-2.2.1-py2.py3-none-any.whl", hash = "sha256:6b0ac9e93fb0335014d382b8fa9b3afa7df546984258005da0b9e7095b3deb1c"}, + {file = "asttokens-2.2.1.tar.gz", hash = "sha256:4622110b2a6f30b77e1473affaa97e711bc2f07d3f10848420ff1898edbe94f3"}, +] + +[package.dependencies] +six = "*" + +[package.extras] +test = ["astroid", "pytest"] + +[[package]] +name = "attrs" +version = "22.2.0" +description = "Classes Without Boilerplate" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "attrs-22.2.0-py3-none-any.whl", hash = "sha256:29e95c7f6778868dbd49170f98f8818f78f3dc5e0e37c0b1f474e3561b240836"}, + {file = "attrs-22.2.0.tar.gz", hash = "sha256:c9227bfc2f01993c03f68db37d1d15c9690188323c067c641f1a35ca58185f99"}, +] + +[package.extras] +cov = ["attrs[tests]", "coverage-enable-subprocess", "coverage[toml] (>=5.3)"] +dev = ["attrs[docs,tests]"] +docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope.interface"] +tests = ["attrs[tests-no-zope]", "zope.interface"] +tests-no-zope = ["cloudpickle", "cloudpickle", "hypothesis", "hypothesis", "mypy (>=0.971,<0.990)", "mypy (>=0.971,<0.990)", "pympler", "pympler", "pytest (>=4.3.0)", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-mypy-plugins", "pytest-xdist[psutil]", "pytest-xdist[psutil]"] + +[[package]] +name = "autorom" +version = "0.4.2" +description = "Automated installation of Atari ROMs for Gym/ALE-Py" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "AutoROM-0.4.2-py3-none-any.whl", hash = "sha256:719c9d363ef08391fdb7003d70df235b68f36de628d289a946c4a59a3adefa13"}, + {file = "AutoROM-0.4.2.tar.gz", hash = "sha256:b426a39bc0ee3781c7791f28963a9b2e4385b6421eeaf2f368edc00c761d428a"}, +] + +[package.dependencies] +"AutoROM.accept-rom-license" = {version = "*", optional = true, markers = "extra == \"accept-rom-license\""} +click = "*" +requests = "*" +tqdm = "*" + +[package.extras] +accept-rom-license = ["AutoROM.accept-rom-license"] + +[[package]] +name = "autorom-accept-rom-license" +version = "0.4.2" +description = "Automated installation of Atari ROMs for Gym/ALE-Py" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "AutoROM.accept-rom-license-0.4.2.tar.gz", hash = "sha256:f08654c9d2a6837c5ec951c0364bda352510920ea0523005a2d4460c7d2be7f2"}, +] + +[package.dependencies] +click = "*" +requests = "*" +tqdm = "*" + +[package.extras] +tests = ["ale_py", "multi_agent_ale_py"] + +[[package]] +name = "backcall" +version = "0.2.0" +description = "Specifications for callback functions passed in to an API" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"}, + {file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"}, +] + +[[package]] +name = "beautifulsoup4" +version = "4.11.1" +description = "Screen-scraping library" +category = "main" +optional = false +python-versions = ">=3.6.0" +files = [ + {file = "beautifulsoup4-4.11.1-py3-none-any.whl", hash = "sha256:58d5c3d29f5a36ffeb94f02f0d786cd53014cf9b3b3951d42e0080d8a9498d30"}, + {file = "beautifulsoup4-4.11.1.tar.gz", hash = "sha256:ad9aa55b65ef2808eb405f46cf74df7fcb7044d5cbc26487f96eb2ef2e436693"}, +] + +[package.dependencies] +soupsieve = ">1.2" + +[package.extras] +html5lib = ["html5lib"] +lxml = ["lxml"] + +[[package]] +name = "bleach" +version = "5.0.1" +description = "An easy safelist-based HTML-sanitizing tool." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "bleach-5.0.1-py3-none-any.whl", hash = "sha256:085f7f33c15bd408dd9b17a4ad77c577db66d76203e5984b1bd59baeee948b2a"}, + {file = "bleach-5.0.1.tar.gz", hash = "sha256:0d03255c47eb9bd2f26aa9bb7f2107732e7e8fe195ca2f64709fcf3b0a4a085c"}, +] + +[package.dependencies] +six = ">=1.9.0" +webencodings = "*" + +[package.extras] +css = ["tinycss2 (>=1.1.0,<1.2)"] +dev = ["Sphinx (==4.3.2)", "black (==22.3.0)", "build (==0.8.0)", "flake8 (==4.0.1)", "hashin (==0.17.0)", "mypy (==0.961)", "pip-tools (==6.6.2)", "pytest (==7.1.2)", "tox (==3.25.0)", "twine (==4.0.1)", "wheel (==0.37.1)"] + +[[package]] +name = "box2d-py" +version = "2.3.5" +description = "Python Box2D" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "box2d-py-2.3.5.tar.gz", hash = "sha256:b37dc38844bcd7def48a97111d2b082e4f81cca3cece7460feb3eacda0da2207"}, + {file = "box2d_py-2.3.5-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:287aa54005c0644b47bf7ad72966e4068d66e56bcf8458f5b4a653ffe42a2618"}, + {file = "box2d_py-2.3.5-cp36-cp36m-macosx_10_13_x86_64.whl", hash = "sha256:483b3f9acd5d156b72bf2013f93cf7f8ca0ee1562e43d2353ab4c0cbec4ee49a"}, + {file = "box2d_py-2.3.5-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:a294c2d7cc73cc05dd491287079e15419eb98caa3158df94f40faf85eeb4b6e9"}, + {file = "box2d_py-2.3.5-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:0d46068eb8d29e366ed698ab2a4833d4d2d34ed035ebd6a685888007dda05f64"}, +] + +[[package]] +name = "cachecontrol" +version = "0.12.11" +description = "httplib2 caching for requests" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "CacheControl-0.12.11-py2.py3-none-any.whl", hash = "sha256:2c75d6a8938cb1933c75c50184549ad42728a27e9f6b92fd677c3151aa72555b"}, + {file = "CacheControl-0.12.11.tar.gz", hash = "sha256:a5b9fcc986b184db101aa280b42ecdcdfc524892596f606858e0b7a8b4d9e144"}, +] + +[package.dependencies] +lockfile = {version = ">=0.9", optional = true, markers = "extra == \"filecache\""} +msgpack = ">=0.5.2" +requests = "*" + +[package.extras] +filecache = ["lockfile (>=0.9)"] +redis = ["redis (>=2.10.5)"] + +[[package]] +name = "cachetools" +version = "5.2.1" +description = "Extensible memoizing collections and decorators" +category = "main" +optional = false +python-versions = "~=3.7" +files = [ + {file = "cachetools-5.2.1-py3-none-any.whl", hash = "sha256:8462eebf3a6c15d25430a8c27c56ac61340b2ecf60c9ce57afc2b97e450e47da"}, + {file = "cachetools-5.2.1.tar.gz", hash = "sha256:5991bc0e08a1319bb618d3195ca5b6bc76646a49c21d55962977197b301cc1fe"}, +] + +[[package]] +name = "cachy" +version = "0.3.0" +description = "Cachy provides a simple yet effective caching library." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "cachy-0.3.0-py2.py3-none-any.whl", hash = "sha256:338ca09c8860e76b275aff52374330efedc4d5a5e45dc1c5b539c1ead0786fe7"}, + {file = "cachy-0.3.0.tar.gz", hash = "sha256:186581f4ceb42a0bbe040c407da73c14092379b1e4c0e327fdb72ae4a9b269b1"}, +] + +[package.extras] +memcached = ["python-memcached (>=1.59,<2.0)"] +msgpack = ["msgpack-python (>=0.5,<0.6)"] +redis = ["redis (>=3.3.6,<4.0.0)"] + +[[package]] +name = "certifi" +version = "2022.12.7" +description = "Python package for providing Mozilla's CA Bundle." +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2022.12.7-py3-none-any.whl", hash = "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"}, + {file = "certifi-2022.12.7.tar.gz", hash = "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3"}, +] + +[[package]] +name = "cffi" +version = "1.15.1" +description = "Foreign Function Interface for Python calling C code." +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"}, + {file = "cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"}, + {file = "cffi-1.15.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914"}, + {file = "cffi-1.15.1-cp27-cp27m-win32.whl", hash = "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3"}, + {file = "cffi-1.15.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e"}, + {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162"}, + {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b"}, + {file = "cffi-1.15.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21"}, + {file = "cffi-1.15.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4"}, + {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01"}, + {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e"}, + {file = "cffi-1.15.1-cp310-cp310-win32.whl", hash = "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2"}, + {file = "cffi-1.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d"}, + {file = "cffi-1.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac"}, + {file = "cffi-1.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c"}, + {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef"}, + {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8"}, + {file = "cffi-1.15.1-cp311-cp311-win32.whl", hash = "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d"}, + {file = "cffi-1.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104"}, + {file = "cffi-1.15.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e"}, + {file = "cffi-1.15.1-cp36-cp36m-win32.whl", hash = "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf"}, + {file = "cffi-1.15.1-cp36-cp36m-win_amd64.whl", hash = "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497"}, + {file = "cffi-1.15.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426"}, + {file = "cffi-1.15.1-cp37-cp37m-win32.whl", hash = "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9"}, + {file = "cffi-1.15.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045"}, + {file = "cffi-1.15.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192"}, + {file = "cffi-1.15.1-cp38-cp38-win32.whl", hash = "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314"}, + {file = "cffi-1.15.1-cp38-cp38-win_amd64.whl", hash = "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5"}, + {file = "cffi-1.15.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585"}, + {file = "cffi-1.15.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27"}, + {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76"}, + {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3"}, + {file = "cffi-1.15.1-cp39-cp39-win32.whl", hash = "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee"}, + {file = "cffi-1.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c"}, + {file = "cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"}, +] + +[package.dependencies] +pycparser = "*" + +[[package]] +name = "charset-normalizer" +version = "3.0.1" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "charset-normalizer-3.0.1.tar.gz", hash = "sha256:ebea339af930f8ca5d7a699b921106c6e29c617fe9606fa7baa043c1cdae326f"}, + {file = "charset_normalizer-3.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:88600c72ef7587fe1708fd242b385b6ed4b8904976d5da0893e31df8b3480cb6"}, + {file = "charset_normalizer-3.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c75ffc45f25324e68ab238cb4b5c0a38cd1c3d7f1fb1f72b5541de469e2247db"}, + {file = "charset_normalizer-3.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:db72b07027db150f468fbada4d85b3b2729a3db39178abf5c543b784c1254539"}, + {file = "charset_normalizer-3.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62595ab75873d50d57323a91dd03e6966eb79c41fa834b7a1661ed043b2d404d"}, + {file = "charset_normalizer-3.0.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ff6f3db31555657f3163b15a6b7c6938d08df7adbfc9dd13d9d19edad678f1e8"}, + {file = "charset_normalizer-3.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:772b87914ff1152b92a197ef4ea40efe27a378606c39446ded52c8f80f79702e"}, + {file = "charset_normalizer-3.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70990b9c51340e4044cfc394a81f614f3f90d41397104d226f21e66de668730d"}, + {file = "charset_normalizer-3.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:292d5e8ba896bbfd6334b096e34bffb56161c81408d6d036a7dfa6929cff8783"}, + {file = "charset_normalizer-3.0.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:2edb64ee7bf1ed524a1da60cdcd2e1f6e2b4f66ef7c077680739f1641f62f555"}, + {file = "charset_normalizer-3.0.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:31a9ddf4718d10ae04d9b18801bd776693487cbb57d74cc3458a7673f6f34639"}, + {file = "charset_normalizer-3.0.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:44ba614de5361b3e5278e1241fda3dc1838deed864b50a10d7ce92983797fa76"}, + {file = "charset_normalizer-3.0.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:12db3b2c533c23ab812c2b25934f60383361f8a376ae272665f8e48b88e8e1c6"}, + {file = "charset_normalizer-3.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c512accbd6ff0270939b9ac214b84fb5ada5f0409c44298361b2f5e13f9aed9e"}, + {file = "charset_normalizer-3.0.1-cp310-cp310-win32.whl", hash = "sha256:502218f52498a36d6bf5ea77081844017bf7982cdbe521ad85e64cabee1b608b"}, + {file = "charset_normalizer-3.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:601f36512f9e28f029d9481bdaf8e89e5148ac5d89cffd3b05cd533eeb423b59"}, + {file = "charset_normalizer-3.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0298eafff88c99982a4cf66ba2efa1128e4ddaca0b05eec4c456bbc7db691d8d"}, + {file = "charset_normalizer-3.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a8d0fc946c784ff7f7c3742310cc8a57c5c6dc31631269876a88b809dbeff3d3"}, + {file = "charset_normalizer-3.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:87701167f2a5c930b403e9756fab1d31d4d4da52856143b609e30a1ce7160f3c"}, + {file = "charset_normalizer-3.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:14e76c0f23218b8f46c4d87018ca2e441535aed3632ca134b10239dfb6dadd6b"}, + {file = "charset_normalizer-3.0.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0c0a590235ccd933d9892c627dec5bc7511ce6ad6c1011fdf5b11363022746c1"}, + {file = "charset_normalizer-3.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8c7fe7afa480e3e82eed58e0ca89f751cd14d767638e2550c77a92a9e749c317"}, + {file = "charset_normalizer-3.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:79909e27e8e4fcc9db4addea88aa63f6423ebb171db091fb4373e3312cb6d603"}, + {file = "charset_normalizer-3.0.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ac7b6a045b814cf0c47f3623d21ebd88b3e8cf216a14790b455ea7ff0135d18"}, + {file = "charset_normalizer-3.0.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:72966d1b297c741541ca8cf1223ff262a6febe52481af742036a0b296e35fa5a"}, + {file = "charset_normalizer-3.0.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:f9d0c5c045a3ca9bedfc35dca8526798eb91a07aa7a2c0fee134c6c6f321cbd7"}, + {file = "charset_normalizer-3.0.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:5995f0164fa7df59db4746112fec3f49c461dd6b31b841873443bdb077c13cfc"}, + {file = "charset_normalizer-3.0.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4a8fcf28c05c1f6d7e177a9a46a1c52798bfe2ad80681d275b10dcf317deaf0b"}, + {file = "charset_normalizer-3.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:761e8904c07ad053d285670f36dd94e1b6ab7f16ce62b9805c475b7aa1cffde6"}, + {file = "charset_normalizer-3.0.1-cp311-cp311-win32.whl", hash = "sha256:71140351489970dfe5e60fc621ada3e0f41104a5eddaca47a7acb3c1b851d6d3"}, + {file = "charset_normalizer-3.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:9ab77acb98eba3fd2a85cd160851816bfce6871d944d885febf012713f06659c"}, + {file = "charset_normalizer-3.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:84c3990934bae40ea69a82034912ffe5a62c60bbf6ec5bc9691419641d7d5c9a"}, + {file = "charset_normalizer-3.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:74292fc76c905c0ef095fe11e188a32ebd03bc38f3f3e9bcb85e4e6db177b7ea"}, + {file = "charset_normalizer-3.0.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c95a03c79bbe30eec3ec2b7f076074f4281526724c8685a42872974ef4d36b72"}, + {file = "charset_normalizer-3.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f4c39b0e3eac288fedc2b43055cfc2ca7a60362d0e5e87a637beac5d801ef478"}, + {file = "charset_normalizer-3.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df2c707231459e8a4028eabcd3cfc827befd635b3ef72eada84ab13b52e1574d"}, + {file = "charset_normalizer-3.0.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:93ad6d87ac18e2a90b0fe89df7c65263b9a99a0eb98f0a3d2e079f12a0735837"}, + {file = "charset_normalizer-3.0.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:59e5686dd847347e55dffcc191a96622f016bc0ad89105e24c14e0d6305acbc6"}, + {file = "charset_normalizer-3.0.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:cd6056167405314a4dc3c173943f11249fa0f1b204f8b51ed4bde1a9cd1834dc"}, + {file = "charset_normalizer-3.0.1-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:083c8d17153ecb403e5e1eb76a7ef4babfc2c48d58899c98fcaa04833e7a2f9a"}, + {file = "charset_normalizer-3.0.1-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:f5057856d21e7586765171eac8b9fc3f7d44ef39425f85dbcccb13b3ebea806c"}, + {file = "charset_normalizer-3.0.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:7eb33a30d75562222b64f569c642ff3dc6689e09adda43a082208397f016c39a"}, + {file = "charset_normalizer-3.0.1-cp36-cp36m-win32.whl", hash = "sha256:95dea361dd73757c6f1c0a1480ac499952c16ac83f7f5f4f84f0658a01b8ef41"}, + {file = "charset_normalizer-3.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:eaa379fcd227ca235d04152ca6704c7cb55564116f8bc52545ff357628e10602"}, + {file = "charset_normalizer-3.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3e45867f1f2ab0711d60c6c71746ac53537f1684baa699f4f668d4c6f6ce8e14"}, + {file = "charset_normalizer-3.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cadaeaba78750d58d3cc6ac4d1fd867da6fc73c88156b7a3212a3cd4819d679d"}, + {file = "charset_normalizer-3.0.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:911d8a40b2bef5b8bbae2e36a0b103f142ac53557ab421dc16ac4aafee6f53dc"}, + {file = "charset_normalizer-3.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:503e65837c71b875ecdd733877d852adbc465bd82c768a067badd953bf1bc5a3"}, + {file = "charset_normalizer-3.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a60332922359f920193b1d4826953c507a877b523b2395ad7bc716ddd386d866"}, + {file = "charset_normalizer-3.0.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:16a8663d6e281208d78806dbe14ee9903715361cf81f6d4309944e4d1e59ac5b"}, + {file = "charset_normalizer-3.0.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:a16418ecf1329f71df119e8a65f3aa68004a3f9383821edcb20f0702934d8087"}, + {file = "charset_normalizer-3.0.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:9d9153257a3f70d5f69edf2325357251ed20f772b12e593f3b3377b5f78e7ef8"}, + {file = "charset_normalizer-3.0.1-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:02a51034802cbf38db3f89c66fb5d2ec57e6fe7ef2f4a44d070a593c3688667b"}, + {file = "charset_normalizer-3.0.1-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:2e396d70bc4ef5325b72b593a72c8979999aa52fb8bcf03f701c1b03e1166918"}, + {file = "charset_normalizer-3.0.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:11b53acf2411c3b09e6af37e4b9005cba376c872503c8f28218c7243582df45d"}, + {file = "charset_normalizer-3.0.1-cp37-cp37m-win32.whl", hash = "sha256:0bf2dae5291758b6f84cf923bfaa285632816007db0330002fa1de38bfcb7154"}, + {file = "charset_normalizer-3.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:2c03cc56021a4bd59be889c2b9257dae13bf55041a3372d3295416f86b295fb5"}, + {file = "charset_normalizer-3.0.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:024e606be3ed92216e2b6952ed859d86b4cfa52cd5bc5f050e7dc28f9b43ec42"}, + {file = "charset_normalizer-3.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4b0d02d7102dd0f997580b51edc4cebcf2ab6397a7edf89f1c73b586c614272c"}, + {file = "charset_normalizer-3.0.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:358a7c4cb8ba9b46c453b1dd8d9e431452d5249072e4f56cfda3149f6ab1405e"}, + {file = "charset_normalizer-3.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81d6741ab457d14fdedc215516665050f3822d3e56508921cc7239f8c8e66a58"}, + {file = "charset_normalizer-3.0.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8b8af03d2e37866d023ad0ddea594edefc31e827fee64f8de5611a1dbc373174"}, + {file = "charset_normalizer-3.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9cf4e8ad252f7c38dd1f676b46514f92dc0ebeb0db5552f5f403509705e24753"}, + {file = "charset_normalizer-3.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e696f0dd336161fca9adbb846875d40752e6eba585843c768935ba5c9960722b"}, + {file = "charset_normalizer-3.0.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c22d3fe05ce11d3671297dc8973267daa0f938b93ec716e12e0f6dee81591dc1"}, + {file = "charset_normalizer-3.0.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:109487860ef6a328f3eec66f2bf78b0b72400280d8f8ea05f69c51644ba6521a"}, + {file = "charset_normalizer-3.0.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:37f8febc8ec50c14f3ec9637505f28e58d4f66752207ea177c1d67df25da5aed"}, + {file = "charset_normalizer-3.0.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:f97e83fa6c25693c7a35de154681fcc257c1c41b38beb0304b9c4d2d9e164479"}, + {file = "charset_normalizer-3.0.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a152f5f33d64a6be73f1d30c9cc82dfc73cec6477ec268e7c6e4c7d23c2d2291"}, + {file = "charset_normalizer-3.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:39049da0ffb96c8cbb65cbf5c5f3ca3168990adf3551bd1dee10c48fce8ae820"}, + {file = "charset_normalizer-3.0.1-cp38-cp38-win32.whl", hash = "sha256:4457ea6774b5611f4bed5eaa5df55f70abde42364d498c5134b7ef4c6958e20e"}, + {file = "charset_normalizer-3.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:e62164b50f84e20601c1ff8eb55620d2ad25fb81b59e3cd776a1902527a788af"}, + {file = "charset_normalizer-3.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8eade758719add78ec36dc13201483f8e9b5d940329285edcd5f70c0a9edbd7f"}, + {file = "charset_normalizer-3.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8499ca8f4502af841f68135133d8258f7b32a53a1d594aa98cc52013fff55678"}, + {file = "charset_normalizer-3.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3fc1c4a2ffd64890aebdb3f97e1278b0cc72579a08ca4de8cd2c04799a3a22be"}, + {file = "charset_normalizer-3.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00d3ffdaafe92a5dc603cb9bd5111aaa36dfa187c8285c543be562e61b755f6b"}, + {file = "charset_normalizer-3.0.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c2ac1b08635a8cd4e0cbeaf6f5e922085908d48eb05d44c5ae9eabab148512ca"}, + {file = "charset_normalizer-3.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f6f45710b4459401609ebebdbcfb34515da4fc2aa886f95107f556ac69a9147e"}, + {file = "charset_normalizer-3.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ae1de54a77dc0d6d5fcf623290af4266412a7c4be0b1ff7444394f03f5c54e3"}, + {file = "charset_normalizer-3.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3b590df687e3c5ee0deef9fc8c547d81986d9a1b56073d82de008744452d6541"}, + {file = "charset_normalizer-3.0.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ab5de034a886f616a5668aa5d098af2b5385ed70142090e2a31bcbd0af0fdb3d"}, + {file = "charset_normalizer-3.0.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9cb3032517f1627cc012dbc80a8ec976ae76d93ea2b5feaa9d2a5b8882597579"}, + {file = "charset_normalizer-3.0.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:608862a7bf6957f2333fc54ab4399e405baad0163dc9f8d99cb236816db169d4"}, + {file = "charset_normalizer-3.0.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:0f438ae3532723fb6ead77e7c604be7c8374094ef4ee2c5e03a3a17f1fca256c"}, + {file = "charset_normalizer-3.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:356541bf4381fa35856dafa6a965916e54bed415ad8a24ee6de6e37deccf2786"}, + {file = "charset_normalizer-3.0.1-cp39-cp39-win32.whl", hash = "sha256:39cf9ed17fe3b1bc81f33c9ceb6ce67683ee7526e65fde1447c772afc54a1bb8"}, + {file = "charset_normalizer-3.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:0a11e971ed097d24c534c037d298ad32c6ce81a45736d31e0ff0ad37ab437d59"}, + {file = "charset_normalizer-3.0.1-py3-none-any.whl", hash = "sha256:7e189e2e1d3ed2f4aebabd2d5b0f931e883676e51c7624826e0a4e5fe8a0bf24"}, +] + +[[package]] +name = "click" +version = "8.1.3" +description = "Composable command line interface toolkit" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, + {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "click-default-group" +version = "1.2.2" +description = "Extends click.Group to invoke a command without explicit subcommand name" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "click-default-group-1.2.2.tar.gz", hash = "sha256:d9560e8e8dfa44b3562fbc9425042a0fd6d21956fcc2db0077f63f34253ab904"}, +] + +[package.dependencies] +click = "*" + +[[package]] +name = "clikit" +version = "0.6.2" +description = "CliKit is a group of utilities to build beautiful and testable command line interfaces." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "clikit-0.6.2-py2.py3-none-any.whl", hash = "sha256:71268e074e68082306e23d7369a7b99f824a0ef926e55ba2665e911f7208489e"}, + {file = "clikit-0.6.2.tar.gz", hash = "sha256:442ee5db9a14120635c5990bcdbfe7c03ada5898291f0c802f77be71569ded59"}, +] + +[package.dependencies] +crashtest = {version = ">=0.3.0,<0.4.0", markers = "python_version >= \"3.6\" and python_version < \"4.0\""} +pastel = ">=0.2.0,<0.3.0" +pylev = ">=1.3,<2.0" + +[[package]] +name = "cloudpickle" +version = "2.2.0" +description = "Extended pickling support for Python objects" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "cloudpickle-2.2.0-py3-none-any.whl", hash = "sha256:7428798d5926d8fcbfd092d18d01a2a03daf8237d8fcdc8095d256b8490796f0"}, + {file = "cloudpickle-2.2.0.tar.gz", hash = "sha256:3f4219469c55453cfe4737e564b67c2a149109dabf7f242478948b895f61106f"}, +] + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +category = "main" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "comm" +version = "0.1.2" +description = "Jupyter Python Comm implementation, for usage in ipykernel, xeus-python etc." +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "comm-0.1.2-py3-none-any.whl", hash = "sha256:9f3abf3515112fa7c55a42a6a5ab358735c9dccc8b5910a9d8e3ef5998130666"}, + {file = "comm-0.1.2.tar.gz", hash = "sha256:3e2f5826578e683999b93716285b3b1f344f157bf75fa9ce0a797564e742f062"}, +] + +[package.dependencies] +traitlets = ">=5.3" + +[package.extras] +test = ["pytest"] + +[[package]] +name = "commonmark" +version = "0.9.1" +description = "Python parser for the CommonMark Markdown spec" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "commonmark-0.9.1-py2.py3-none-any.whl", hash = "sha256:da2f38c92590f83de410ba1a3cbceafbc74fee9def35f9251ba9a971d6d66fd9"}, + {file = "commonmark-0.9.1.tar.gz", hash = "sha256:452f9dc859be7f06631ddcb328b6919c67984aca654e5fefb3914d54691aed60"}, +] + +[package.extras] +test = ["flake8 (==3.7.8)", "hypothesis (==3.55.3)"] + +[[package]] +name = "conda-lock" +version = "1.3.0" +description = "Lockfiles for conda" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "conda_lock-1.3.0-py3-none-any.whl", hash = "sha256:a4d5676bb6a422f17d4f4485de2c84ba9bf3d95592655382c95e6e76203d83d8"}, + {file = "conda_lock-1.3.0.tar.gz", hash = "sha256:8cf4b3a4e6037dadd74cc2b5325e2e76e0a42f3da31b5fd657a8030bce74ee06"}, +] + +[package.dependencies] +cachecontrol = {version = ">=0.12.9", extras = ["filecache"]} +cachy = ">=0.3.0" +click = ">=8.0" +click-default-group = "*" +clikit = ">=0.6.2" +crashtest = ">=0.3.0" +ensureconda = ">=1.3" +gitpython = "*" +html5lib = ">=1.0" +jinja2 = "*" +keyring = ">=21.2.0" +packaging = ">=20.4" +pkginfo = ">=1.4" +pydantic = ">=1.8.1" +pyyaml = ">=5.1" +requests = ">=2.18" +ruamel-yaml = "*" +tomli = {version = "*", markers = "python_version < \"3.11\""} +tomlkit = ">=0.7.0" +toolz = ">=0.12.0,<1.0.0" +typing-extensions = "*" +virtualenv = ">=20.0.26" + +[package.extras] +pip-support = ["poetry (>=1.1.0,<1.2.0)"] + +[[package]] +name = "contourpy" +version = "1.0.6" +description = "Python library for calculating contours of 2D quadrilateral grids" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "contourpy-1.0.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:613c665529899b5d9fade7e5d1760111a0b011231277a0d36c49f0d3d6914bd6"}, + {file = "contourpy-1.0.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:78ced51807ccb2f45d4ea73aca339756d75d021069604c2fccd05390dc3c28eb"}, + {file = "contourpy-1.0.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b3b1bd7577c530eaf9d2bc52d1a93fef50ac516a8b1062c3d1b9bcec9ebe329b"}, + {file = "contourpy-1.0.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8834c14b8c3dd849005e06703469db9bf96ba2d66a3f88ecc539c9a8982e0ee"}, + {file = "contourpy-1.0.6-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f4052a8a4926d4468416fc7d4b2a7b2a3e35f25b39f4061a7e2a3a2748c4fc48"}, + {file = "contourpy-1.0.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c0e1308307a75e07d1f1b5f0f56b5af84538a5e9027109a7bcf6cb47c434e72"}, + {file = "contourpy-1.0.6-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9fc4e7973ed0e1fe689435842a6e6b330eb7ccc696080dda9a97b1a1b78e41db"}, + {file = "contourpy-1.0.6-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:08e8d09d96219ace6cb596506fb9b64ea5f270b2fb9121158b976d88871fcfd1"}, + {file = "contourpy-1.0.6-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f33da6b5d19ad1bb5e7ad38bb8ba5c426d2178928bc2b2c44e8823ea0ecb6ff3"}, + {file = "contourpy-1.0.6-cp310-cp310-win32.whl", hash = "sha256:12a7dc8439544ed05c6553bf026d5e8fa7fad48d63958a95d61698df0e00092b"}, + {file = "contourpy-1.0.6-cp310-cp310-win_amd64.whl", hash = "sha256:eadad75bf91897f922e0fb3dca1b322a58b1726a953f98c2e5f0606bd8408621"}, + {file = "contourpy-1.0.6-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:913bac9d064cff033cf3719e855d4f1db9f1c179e0ecf3ba9fdef21c21c6a16a"}, + {file = "contourpy-1.0.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:46deb310a276cc5c1fd27958e358cce68b1e8a515fa5a574c670a504c3a3fe30"}, + {file = "contourpy-1.0.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b64f747e92af7da3b85631a55d68c45a2d728b4036b03cdaba4bd94bcc85bd6f"}, + {file = "contourpy-1.0.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50627bf76abb6ba291ad08db583161939c2c5fab38c38181b7833423ab9c7de3"}, + {file = "contourpy-1.0.6-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:358f6364e4873f4d73360b35da30066f40387dd3c427a3e5432c6b28dd24a8fa"}, + {file = "contourpy-1.0.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c78bfbc1a7bff053baf7e508449d2765964d67735c909b583204e3240a2aca45"}, + {file = "contourpy-1.0.6-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e43255a83835a129ef98f75d13d643844d8c646b258bebd11e4a0975203e018f"}, + {file = "contourpy-1.0.6-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:375d81366afd547b8558c4720337218345148bc2fcffa3a9870cab82b29667f2"}, + {file = "contourpy-1.0.6-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b98c820608e2dca6442e786817f646d11057c09a23b68d2b3737e6dcb6e4a49b"}, + {file = "contourpy-1.0.6-cp311-cp311-win32.whl", hash = "sha256:0e4854cc02006ad6684ce092bdadab6f0912d131f91c2450ce6dbdea78ee3c0b"}, + {file = "contourpy-1.0.6-cp311-cp311-win_amd64.whl", hash = "sha256:d2eff2af97ea0b61381828b1ad6cd249bbd41d280e53aea5cccd7b2b31b8225c"}, + {file = "contourpy-1.0.6-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5b117d29433fc8393b18a696d794961464e37afb34a6eeb8b2c37b5f4128a83e"}, + {file = "contourpy-1.0.6-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:341330ed19074f956cb20877ad8d2ae50e458884bfa6a6df3ae28487cc76c768"}, + {file = "contourpy-1.0.6-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:371f6570a81dfdddbb837ba432293a63b4babb942a9eb7aaa699997adfb53278"}, + {file = "contourpy-1.0.6-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9447c45df407d3ecb717d837af3b70cfef432138530712263730783b3d016512"}, + {file = "contourpy-1.0.6-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:730c27978a0003b47b359935478b7d63fd8386dbb2dcd36c1e8de88cbfc1e9de"}, + {file = "contourpy-1.0.6-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:da1ef35fd79be2926ba80fbb36327463e3656c02526e9b5b4c2b366588b74d9a"}, + {file = "contourpy-1.0.6-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:cd2bc0c8f2e8de7dd89a7f1c10b8844e291bca17d359373203ef2e6100819edd"}, + {file = "contourpy-1.0.6-cp37-cp37m-win32.whl", hash = "sha256:3a1917d3941dd58732c449c810fa7ce46cc305ce9325a11261d740118b85e6f3"}, + {file = "contourpy-1.0.6-cp37-cp37m-win_amd64.whl", hash = "sha256:06ca79e1efbbe2df795822df2fa173d1a2b38b6e0f047a0ec7903fbca1d1847e"}, + {file = "contourpy-1.0.6-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e626cefff8491bce356221c22af5a3ea528b0b41fbabc719c00ae233819ea0bf"}, + {file = "contourpy-1.0.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:dbe6fe7a1166b1ddd7b6d887ea6fa8389d3f28b5ed3f73a8f40ece1fc5a3d340"}, + {file = "contourpy-1.0.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e13b31d1b4b68db60b3b29f8e337908f328c7f05b9add4b1b5c74e0691180109"}, + {file = "contourpy-1.0.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a79d239fc22c3b8d9d3de492aa0c245533f4f4c7608e5749af866949c0f1b1b9"}, + {file = "contourpy-1.0.6-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9e8e686a6db92a46111a1ee0ee6f7fbfae4048f0019de207149f43ac1812cf95"}, + {file = "contourpy-1.0.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:acd2bd02f1a7adff3a1f33e431eb96ab6d7987b039d2946a9b39fe6fb16a1036"}, + {file = "contourpy-1.0.6-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:03d1b9c6b44a9e30d554654c72be89af94fab7510b4b9f62356c64c81cec8b7d"}, + {file = "contourpy-1.0.6-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:b48d94386f1994db7c70c76b5808c12e23ed7a4ee13693c2fc5ab109d60243c0"}, + {file = "contourpy-1.0.6-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:208bc904889c910d95aafcf7be9e677726df9ef71e216780170dbb7e37d118fa"}, + {file = "contourpy-1.0.6-cp38-cp38-win32.whl", hash = "sha256:444fb776f58f4906d8d354eb6f6ce59d0a60f7b6a720da6c1ccb839db7c80eb9"}, + {file = "contourpy-1.0.6-cp38-cp38-win_amd64.whl", hash = "sha256:9bc407a6af672da20da74823443707e38ece8b93a04009dca25856c2d9adadb1"}, + {file = "contourpy-1.0.6-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:aa4674cf3fa2bd9c322982644967f01eed0c91bb890f624e0e0daf7a5c3383e9"}, + {file = "contourpy-1.0.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6f56515e7c6fae4529b731f6c117752247bef9cdad2b12fc5ddf8ca6a50965a5"}, + {file = "contourpy-1.0.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:344cb3badf6fc7316ad51835f56ac387bdf86c8e1b670904f18f437d70da4183"}, + {file = "contourpy-1.0.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b1e66346acfb17694d46175a0cea7d9036f12ed0c31dfe86f0f405eedde2bdd"}, + {file = "contourpy-1.0.6-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8468b40528fa1e15181cccec4198623b55dcd58306f8815a793803f51f6c474a"}, + {file = "contourpy-1.0.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1dedf4c64185a216c35eb488e6f433297c660321275734401760dafaeb0ad5c2"}, + {file = "contourpy-1.0.6-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:494efed2c761f0f37262815f9e3c4bb9917c5c69806abdee1d1cb6611a7174a0"}, + {file = "contourpy-1.0.6-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:75a2e638042118118ab39d337da4c7908c1af74a8464cad59f19fbc5bbafec9b"}, + {file = "contourpy-1.0.6-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a628bba09ba72e472bf7b31018b6281fd4cc903f0888049a3724afba13b6e0b8"}, + {file = "contourpy-1.0.6-cp39-cp39-win32.whl", hash = "sha256:e1739496c2f0108013629aa095cc32a8c6363444361960c07493818d0dea2da4"}, + {file = "contourpy-1.0.6-cp39-cp39-win_amd64.whl", hash = "sha256:a457ee72d9032e86730f62c5eeddf402e732fdf5ca8b13b41772aa8ae13a4563"}, + {file = "contourpy-1.0.6-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d912f0154a20a80ea449daada904a7eb6941c83281a9fab95de50529bfc3a1da"}, + {file = "contourpy-1.0.6-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4081918147fc4c29fad328d5066cfc751da100a1098398742f9f364be63803fc"}, + {file = "contourpy-1.0.6-pp37-pypy37_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0537cc1195245bbe24f2913d1f9211b8f04eb203de9044630abd3664c6cc339c"}, + {file = "contourpy-1.0.6-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dcd556c8fc37a342dd636d7eef150b1399f823a4462f8c968e11e1ebeabee769"}, + {file = "contourpy-1.0.6-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:f6ca38dd8d988eca8f07305125dec6f54ac1c518f1aaddcc14d08c01aebb6efc"}, + {file = "contourpy-1.0.6-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:c1baa49ab9fedbf19d40d93163b7d3e735d9cd8d5efe4cce9907902a6dad391f"}, + {file = "contourpy-1.0.6-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:211dfe2bd43bf5791d23afbe23a7952e8ac8b67591d24be3638cabb648b3a6eb"}, + {file = "contourpy-1.0.6-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c38c6536c2d71ca2f7e418acaf5bca30a3af7f2a2fa106083c7d738337848dbe"}, + {file = "contourpy-1.0.6-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b1ee48a130da4dd0eb8055bbab34abf3f6262957832fd575e0cab4979a15a41"}, + {file = "contourpy-1.0.6-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:5641927cc5ae66155d0c80195dc35726eae060e7defc18b7ab27600f39dd1fe7"}, + {file = "contourpy-1.0.6-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:7ee394502026d68652c2824348a40bf50f31351a668977b51437131a90d777ea"}, + {file = "contourpy-1.0.6-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b97454ed5b1368b66ed414c754cba15b9750ce69938fc6153679787402e4cdf"}, + {file = "contourpy-1.0.6-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0236875c5a0784215b49d00ebbe80c5b6b5d5244b3655a36dda88105334dea17"}, + {file = "contourpy-1.0.6-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:84c593aeff7a0171f639da92cb86d24954bbb61f8a1b530f74eb750a14685832"}, + {file = "contourpy-1.0.6-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:9b0e7fe7f949fb719b206548e5cde2518ffb29936afa4303d8a1c4db43dcb675"}, + {file = "contourpy-1.0.6.tar.gz", hash = "sha256:6e459ebb8bb5ee4c22c19cc000174f8059981971a33ce11e17dddf6aca97a142"}, +] + +[package.dependencies] +numpy = ">=1.16" + +[package.extras] +bokeh = ["bokeh", "selenium"] +docs = ["docutils (<0.18)", "sphinx (<=5.2.0)", "sphinx-rtd-theme"] +test = ["Pillow", "flake8", "isort", "matplotlib", "pytest"] +test-minimal = ["pytest"] +test-no-codebase = ["Pillow", "matplotlib", "pytest"] + +[[package]] +name = "crashtest" +version = "0.3.1" +description = "Manage Python errors with ease" +category = "main" +optional = false +python-versions = ">=3.6,<4.0" +files = [ + {file = "crashtest-0.3.1-py3-none-any.whl", hash = "sha256:300f4b0825f57688b47b6d70c6a31de33512eb2fa1ac614f780939aa0cf91680"}, + {file = "crashtest-0.3.1.tar.gz", hash = "sha256:42ca7b6ce88b6c7433e2ce47ea884e91ec93104a4b754998be498a8e6c3d37dd"}, +] + +[[package]] +name = "cryptography" +version = "39.0.1" +description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "cryptography-39.0.1-cp36-abi3-macosx_10_12_universal2.whl", hash = "sha256:6687ef6d0a6497e2b58e7c5b852b53f62142cfa7cd1555795758934da363a965"}, + {file = "cryptography-39.0.1-cp36-abi3-macosx_10_12_x86_64.whl", hash = "sha256:706843b48f9a3f9b9911979761c91541e3d90db1ca905fd63fee540a217698bc"}, + {file = "cryptography-39.0.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:5d2d8b87a490bfcd407ed9d49093793d0f75198a35e6eb1a923ce1ee86c62b41"}, + {file = "cryptography-39.0.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:83e17b26de248c33f3acffb922748151d71827d6021d98c70e6c1a25ddd78505"}, + {file = "cryptography-39.0.1-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e124352fd3db36a9d4a21c1aa27fd5d051e621845cb87fb851c08f4f75ce8be6"}, + {file = "cryptography-39.0.1-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:5aa67414fcdfa22cf052e640cb5ddc461924a045cacf325cd164e65312d99502"}, + {file = "cryptography-39.0.1-cp36-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:35f7c7d015d474f4011e859e93e789c87d21f6f4880ebdc29896a60403328f1f"}, + {file = "cryptography-39.0.1-cp36-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f24077a3b5298a5a06a8e0536e3ea9ec60e4c7ac486755e5fb6e6ea9b3500106"}, + {file = "cryptography-39.0.1-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:f0c64d1bd842ca2633e74a1a28033d139368ad959872533b1bab8c80e8240a0c"}, + {file = "cryptography-39.0.1-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:0f8da300b5c8af9f98111ffd512910bc792b4c77392a9523624680f7956a99d4"}, + {file = "cryptography-39.0.1-cp36-abi3-win32.whl", hash = "sha256:fe913f20024eb2cb2f323e42a64bdf2911bb9738a15dba7d3cce48151034e3a8"}, + {file = "cryptography-39.0.1-cp36-abi3-win_amd64.whl", hash = "sha256:ced4e447ae29ca194449a3f1ce132ded8fcab06971ef5f618605aacaa612beac"}, + {file = "cryptography-39.0.1-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:807ce09d4434881ca3a7594733669bd834f5b2c6d5c7e36f8c00f691887042ad"}, + {file = "cryptography-39.0.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:96f1157a7c08b5b189b16b47bc9db2332269d6680a196341bf30046330d15388"}, + {file = "cryptography-39.0.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:e422abdec8b5fa8462aa016786680720d78bdce7a30c652b7fadf83a4ba35336"}, + {file = "cryptography-39.0.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:b0afd054cd42f3d213bf82c629efb1ee5f22eba35bf0eec88ea9ea7304f511a2"}, + {file = "cryptography-39.0.1-pp39-pypy39_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:6f8ba7f0328b79f08bdacc3e4e66fb4d7aab0c3584e0bd41328dce5262e26b2e"}, + {file = "cryptography-39.0.1-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:ef8b72fa70b348724ff1218267e7f7375b8de4e8194d1636ee60510aae104cd0"}, + {file = "cryptography-39.0.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:aec5a6c9864be7df2240c382740fcf3b96928c46604eaa7f3091f58b878c0bb6"}, + {file = "cryptography-39.0.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:fdd188c8a6ef8769f148f88f859884507b954cc64db6b52f66ef199bb9ad660a"}, + {file = "cryptography-39.0.1.tar.gz", hash = "sha256:d1f6198ee6d9148405e49887803907fe8962a23e6c6f83ea7d98f1c0de375695"}, +] + +[package.dependencies] +cffi = ">=1.12" + +[package.extras] +docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"] +docstest = ["pyenchant (>=1.6.11)", "sphinxcontrib-spelling (>=4.0.1)", "twine (>=1.12.0)"] +pep8test = ["black", "check-manifest", "mypy", "ruff", "types-pytz", "types-requests"] +sdist = ["setuptools-rust (>=0.11.4)"] +ssh = ["bcrypt (>=3.1.5)"] +test = ["hypothesis (>=1.11.4,!=3.79.2)", "iso8601", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-shard (>=0.1.2)", "pytest-subtests", "pytest-xdist", "pytz"] +test-randomorder = ["pytest-randomly"] +tox = ["tox"] + +[[package]] +name = "cycler" +version = "0.11.0" +description = "Composable style cycles" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "cycler-0.11.0-py3-none-any.whl", hash = "sha256:3a27e95f763a428a739d2add979fa7494c912a32c17c4c38c4d5f082cad165a3"}, + {file = "cycler-0.11.0.tar.gz", hash = "sha256:9c87405839a19696e837b3b818fed3f5f69f16f1eec1a1ad77e043dcea9c772f"}, +] + +[[package]] +name = "debugpy" +version = "1.6.5" +description = "An implementation of the Debug Adapter Protocol for Python" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "debugpy-1.6.5-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:696165f021a6a17da08163eaae84f3faf5d8be68fb78cd78488dd347e625279c"}, + {file = "debugpy-1.6.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17039e392d6f38388a68bd02c5f823b32a92142a851e96ba3ec52aeb1ce9d900"}, + {file = "debugpy-1.6.5-cp310-cp310-win32.whl", hash = "sha256:62a06eb78378292ba6c427d861246574dc8b84471904973797b29dd33c7c2495"}, + {file = "debugpy-1.6.5-cp310-cp310-win_amd64.whl", hash = "sha256:9984fc00ab372c97f63786c400107f54224663ea293daab7b365a5b821d26309"}, + {file = "debugpy-1.6.5-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:048368f121c08b00bbded161e8583817af5055982d2722450a69efe2051621c2"}, + {file = "debugpy-1.6.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:74e4eca42055759032e3f1909d1374ba1d729143e0c2729bb8cb5e8b5807c458"}, + {file = "debugpy-1.6.5-cp37-cp37m-win32.whl", hash = "sha256:0f9afcc8cad6424695f3356dc9a7406d5b18e37ee2e73f34792881a44b02cc50"}, + {file = "debugpy-1.6.5-cp37-cp37m-win_amd64.whl", hash = "sha256:b5a74ecebe5253344501d9b23f74459c46428b30437fa9254cfb8cb129943242"}, + {file = "debugpy-1.6.5-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:9e809ef787802c808995e5b6ade714a25fa187f892b41a412d418a15a9c4a432"}, + {file = "debugpy-1.6.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:947c686e8adb46726f3d5f19854f6aebf66c2edb91225643c7f44b40b064a235"}, + {file = "debugpy-1.6.5-cp38-cp38-win32.whl", hash = "sha256:377391341c4b86f403d93e467da8e2d05c22b683f08f9af3e16d980165b06b90"}, + {file = "debugpy-1.6.5-cp38-cp38-win_amd64.whl", hash = "sha256:286ae0c2def18ee0dc8a61fa76d51039ca8c11485b6ed3ef83e3efe8a23926ae"}, + {file = "debugpy-1.6.5-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:500dd4a9ff818f5c52dddb4a608c7de5371c2d7d905c505eb745556c579a9f11"}, + {file = "debugpy-1.6.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f3fab217fe7e2acb2d90732af1a871947def4e2b6654945ba1ebd94bd0bea26"}, + {file = "debugpy-1.6.5-cp39-cp39-win32.whl", hash = "sha256:15bc5febe0edc79726517b1f8d57d7ac7c784567b5ba804aab8b1c9d07a57018"}, + {file = "debugpy-1.6.5-cp39-cp39-win_amd64.whl", hash = "sha256:7e84d9e4420122384cb2cc762a00b4e17cbf998022890f89b195ce178f78ff47"}, + {file = "debugpy-1.6.5-py2.py3-none-any.whl", hash = "sha256:8116e40a1cd0593bd2aba01d4d560ee08f018da8e8fbd4cbd24ff09b5f0e41ef"}, + {file = "debugpy-1.6.5.zip", hash = "sha256:5e55e6c79e215239dd0794ee0bf655412b934735a58e9d705e5c544f596f1603"}, +] + +[[package]] +name = "decorator" +version = "5.1.1" +description = "Decorators for Humans" +category = "main" +optional = false +python-versions = ">=3.5" +files = [ + {file = "decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"}, + {file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"}, +] + +[[package]] +name = "defusedxml" +version = "0.7.1" +description = "XML bomb protection for Python stdlib modules" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61"}, + {file = "defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69"}, +] + +[[package]] +name = "distlib" +version = "0.3.6" +description = "Distribution utilities" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "distlib-0.3.6-py2.py3-none-any.whl", hash = "sha256:f35c4b692542ca110de7ef0bea44d73981caeb34ca0b9b6b2e6d7790dda8f80e"}, + {file = "distlib-0.3.6.tar.gz", hash = "sha256:14bad2d9b04d3a36127ac97f30b12a19268f211063d8f8ee4f47108896e11b46"}, +] + +[[package]] +name = "docker-pycreds" +version = "0.4.0" +description = "Python bindings for the docker credentials store API" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "docker-pycreds-0.4.0.tar.gz", hash = "sha256:6ce3270bcaf404cc4c3e27e4b6c70d3521deae82fb508767870fdbf772d584d4"}, + {file = "docker_pycreds-0.4.0-py2.py3-none-any.whl", hash = "sha256:7266112468627868005106ec19cd0d722702d2b7d5912a28e19b826c3d37af49"}, +] + +[package.dependencies] +six = ">=1.4.0" + +[[package]] +name = "ensureconda" +version = "1.4.3" +description = "Lightweight bootstrapper for a conda executable" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "ensureconda-1.4.3-py3-none-any.whl", hash = "sha256:a746630675e8d5a6a3fc1e8d2ec9c80459e1e722df94365a5099eec0079572a8"}, + {file = "ensureconda-1.4.3.tar.gz", hash = "sha256:e04ae2e2f872869df7e7da22dcca9bd7c42f839a55155dddb249bcdc9e6aae48"}, +] + +[package.dependencies] +appdirs = "*" +click = ">=5.1" +filelock = "*" +requests = ">=2" + +[[package]] +name = "entrypoints" +version = "0.4" +description = "Discover and load entry points from installed packages." +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "entrypoints-0.4-py3-none-any.whl", hash = "sha256:f174b5ff827504fd3cd97cc3f8649f3693f51538c7e4bdf3ef002c8429d42f9f"}, + {file = "entrypoints-0.4.tar.gz", hash = "sha256:b706eddaa9218a19ebcd67b56818f05bb27589b1ca9e8d797b74affad4ccacd4"}, +] + +[[package]] +name = "executing" +version = "1.2.0" +description = "Get the currently executing AST node of a frame, and other information" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "executing-1.2.0-py2.py3-none-any.whl", hash = "sha256:0314a69e37426e3608aada02473b4161d4caf5a4b244d1d0c48072b8fee7bacc"}, + {file = "executing-1.2.0.tar.gz", hash = "sha256:19da64c18d2d851112f09c287f8d3dbbdf725ab0e569077efb6cdcbd3497c107"}, +] + +[package.extras] +tests = ["asttokens", "littleutils", "pytest", "rich"] + +[[package]] +name = "fastjsonschema" +version = "2.16.2" +description = "Fastest Python implementation of JSON schema" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "fastjsonschema-2.16.2-py3-none-any.whl", hash = "sha256:21f918e8d9a1a4ba9c22e09574ba72267a6762d47822db9add95f6454e51cc1c"}, + {file = "fastjsonschema-2.16.2.tar.gz", hash = "sha256:01e366f25d9047816fe3d288cbfc3e10541daf0af2044763f3d0ade42476da18"}, +] + +[package.extras] +devel = ["colorama", "json-spec", "jsonschema", "pylint", "pytest", "pytest-benchmark", "pytest-cache", "validictory"] + +[[package]] +name = "filelock" +version = "3.9.0" +description = "A platform independent file lock." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "filelock-3.9.0-py3-none-any.whl", hash = "sha256:f58d535af89bb9ad5cd4df046f741f8553a418c01a7856bf0d173bbc9f6bd16d"}, + {file = "filelock-3.9.0.tar.gz", hash = "sha256:7b319f24340b51f55a2bf7a12ac0755a9b03e718311dac567a0f4f7fabd2f5de"}, +] + +[package.extras] +docs = ["furo (>=2022.12.7)", "sphinx (>=5.3)", "sphinx-autodoc-typehints (>=1.19.5)"] +testing = ["covdefaults (>=2.2.2)", "coverage (>=7.0.1)", "pytest (>=7.2)", "pytest-cov (>=4)", "pytest-timeout (>=2.1)"] + +[[package]] +name = "fonttools" +version = "4.38.0" +description = "Tools to manipulate font files" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "fonttools-4.38.0-py3-none-any.whl", hash = "sha256:820466f43c8be8c3009aef8b87e785014133508f0de64ec469e4efb643ae54fb"}, + {file = "fonttools-4.38.0.zip", hash = "sha256:2bb244009f9bf3fa100fc3ead6aeb99febe5985fa20afbfbaa2f8946c2fbdaf1"}, +] + +[package.extras] +all = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "fs (>=2.2.0,<3)", "lxml (>=4.0,<5)", "lz4 (>=1.7.4.2)", "matplotlib", "munkres", "scipy", "skia-pathops (>=0.5.0)", "sympy", "uharfbuzz (>=0.23.0)", "unicodedata2 (>=14.0.0)", "xattr", "zopfli (>=0.1.4)"] +graphite = ["lz4 (>=1.7.4.2)"] +interpolatable = ["munkres", "scipy"] +lxml = ["lxml (>=4.0,<5)"] +pathops = ["skia-pathops (>=0.5.0)"] +plot = ["matplotlib"] +repacker = ["uharfbuzz (>=0.23.0)"] +symfont = ["sympy"] +type1 = ["xattr"] +ufo = ["fs (>=2.2.0,<3)"] +unicode = ["unicodedata2 (>=14.0.0)"] +woff = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "zopfli (>=0.1.4)"] + +[[package]] +name = "fqdn" +version = "1.5.1" +description = "Validates fully-qualified domain names against RFC 1123, so that they are acceptable to modern bowsers" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0, !=3.1, !=3.2, !=3.3, !=3.4, <4" +files = [ + {file = "fqdn-1.5.1-py3-none-any.whl", hash = "sha256:3a179af3761e4df6eb2e026ff9e1a3033d3587bf980a0b1b2e1e5d08d7358014"}, + {file = "fqdn-1.5.1.tar.gz", hash = "sha256:105ed3677e767fb5ca086a0c1f4bb66ebc3c100be518f0e0d755d9eae164d89f"}, +] + +[[package]] +name = "gitdb" +version = "4.0.10" +description = "Git Object Database" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "gitdb-4.0.10-py3-none-any.whl", hash = "sha256:c286cf298426064079ed96a9e4a9d39e7f3e9bf15ba60701e95f5492f28415c7"}, + {file = "gitdb-4.0.10.tar.gz", hash = "sha256:6eb990b69df4e15bad899ea868dc46572c3f75339735663b81de79b06f17eb9a"}, +] + +[package.dependencies] +smmap = ">=3.0.1,<6" + +[[package]] +name = "gitpython" +version = "3.1.30" +description = "GitPython is a python library used to interact with Git repositories" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "GitPython-3.1.30-py3-none-any.whl", hash = "sha256:cd455b0000615c60e286208ba540271af9fe531fa6a87cc590a7298785ab2882"}, + {file = "GitPython-3.1.30.tar.gz", hash = "sha256:769c2d83e13f5d938b7688479da374c4e3d49f71549aaf462b646db9602ea6f8"}, +] + +[package.dependencies] +gitdb = ">=4.0.1,<5" + +[[package]] +name = "glcontext" +version = "2.3.7" +description = "Portable OpenGL Context" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "glcontext-2.3.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8ece87d8616bf12e55a08a05159f4303c8b82d348c2c43c7297c85d8e95dfa3e"}, + {file = "glcontext-2.3.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b5dcd68b23b1a549a3b0851d3621630e492ff9015a18f29f2512088b4e03e4d9"}, + {file = "glcontext-2.3.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3dc6a6133bffc33cb75bbc79dc08bd1e206017ac69ec68f703227aaf5f5129bb"}, + {file = "glcontext-2.3.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bc906a19be96d2820dee8e681ca1d3129821eb6e5c4f1544db723edf0c0696bd"}, + {file = "glcontext-2.3.7-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89869925f4e1762878561fa1e3cbd1ee5ce73e5597275b5fc8bc054dd894fca4"}, + {file = "glcontext-2.3.7-cp310-cp310-win32.whl", hash = "sha256:088482e07aed6229a34fbb1d0c5fbe0ad9c413dbddb5eaaa8e5c83d933cbe8d6"}, + {file = "glcontext-2.3.7-cp310-cp310-win_amd64.whl", hash = "sha256:03b505fc8ce2dfcf800feac0e20cbb7b1899a5ef7407fa0cccb3267a5b2abbdb"}, + {file = "glcontext-2.3.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:155154084bdedfc8904524d8bd212e5896cc5d5caf1d45c19d13dc34aee4b5ab"}, + {file = "glcontext-2.3.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:65bf63b2068e13183e34a4beaf921f20cd144a25cebed0fa9a46f25e8b47577d"}, + {file = "glcontext-2.3.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:51e04b162529f99c7b764129e07aaa3ec8edfc63ca7a212b71e348319f8b821b"}, + {file = "glcontext-2.3.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0961811d85ac551b1ce1f197296a8e5f497b35a149cfc6e128f74dfaef5e592f"}, + {file = "glcontext-2.3.7-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa4595600a699ed13e854b87116a1519a25e47a10100df01650c1be3532bd629"}, + {file = "glcontext-2.3.7-cp311-cp311-win32.whl", hash = "sha256:7dc827f119ccc3ea55b7bec73573516117c55319edc93bc2bbcf389bf1e7acfe"}, + {file = "glcontext-2.3.7-cp311-cp311-win_amd64.whl", hash = "sha256:a22a3fbb3abefd7a9f5a672af8fccb8d8d996b2eae2075ac9d8ca10f4a6f6653"}, + {file = "glcontext-2.3.7-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6df4cf354adb911a9ca58bc5c60fb1ae27544527878bc3ddf8f7ea56946c6fcc"}, + {file = "glcontext-2.3.7-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f1656e931c937f8bdce12c551fa0077db814b123e7f16b6db26e1e7c89dae16"}, + {file = "glcontext-2.3.7-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:058bf839884b5d5d8488978ed804023be64fc9bafb674a0ede1ba26c05bd9146"}, + {file = "glcontext-2.3.7-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f17be52c99e3eaeefaaac780bde40bfa99be3ad32bbfed346bb347c9d0b01967"}, + {file = "glcontext-2.3.7-cp37-cp37m-win32.whl", hash = "sha256:5a4cc4fef74dcab0b428ef750fad3c05311657ffb4f1dd3d4afa75e664551588"}, + {file = "glcontext-2.3.7-cp37-cp37m-win_amd64.whl", hash = "sha256:fd03d6d8dbfdd9bab97ada98759e345b29d50f690cec95dd01d22d02f616bfea"}, + {file = "glcontext-2.3.7-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:03b3925472771607d13feb9a0de93b04408ae86c91eee3f5e09e43744f90b1af"}, + {file = "glcontext-2.3.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f234ebcc3355155811389c320974056ce20233770205fc7cb41d8653d6137efa"}, + {file = "glcontext-2.3.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:46ef33b616027a616dcceba33bc48e589ba24fa84ee43c5b8611c5b57d2dace3"}, + {file = "glcontext-2.3.7-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ff822473d498d606424f92a341d01121562af35bf1d3d0e2ccd1f9c2f86859b"}, + {file = "glcontext-2.3.7-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87c90b525296c4930b1f74bf460b97af052c3cc9ba47d811f416ed82e1b16b03"}, + {file = "glcontext-2.3.7-cp38-cp38-win32.whl", hash = "sha256:f1444229f84a7aea48ce3f1143147acee92eee264826db4c41ea38c6b0a924a9"}, + {file = "glcontext-2.3.7-cp38-cp38-win_amd64.whl", hash = "sha256:59580776fd7e520995b82a6134c8ca7152a7881e174077fc785f4cc69c476d69"}, + {file = "glcontext-2.3.7-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8aa90a648f17bacacef95b09a5fab368e8feff3714fc4b81eb9374bd439850e6"}, + {file = "glcontext-2.3.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:867fe03c1c241d2416b719e23d1671537e34e03bab741dcc50d49298c1397073"}, + {file = "glcontext-2.3.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae930d226f9145ec580f3fe10fc23262b8c21a6a0cd6fbc081a6606e9000ce74"}, + {file = "glcontext-2.3.7-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cc73099fa7525a20e2021a2f2befa61e9ef306364838c1859ba79f5bd8eda33a"}, + {file = "glcontext-2.3.7-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:755698083c6119e771ea3f5837143324636700e1e5b397885c05085a837d5876"}, + {file = "glcontext-2.3.7-cp39-cp39-win32.whl", hash = "sha256:ab8147607af85fc2ec2e02b4364ff36b636f63781295e74220dc5c5856794e07"}, + {file = "glcontext-2.3.7-cp39-cp39-win_amd64.whl", hash = "sha256:2fae2d4bcb0564e0eb8e72c97e149faebfad369aeaef74ed7fd17f5f84a07428"}, + {file = "glcontext-2.3.7-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:e48550269c3baff04cc46ca79bd9d2d5a62216665751b10aa86d95ebe182d319"}, + {file = "glcontext-2.3.7-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82eff3e5664c5a17fc0cbb1dae2c32088cdd3c3bfbfe4b9c71012275c2a63e8e"}, + {file = "glcontext-2.3.7-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44f7dbf800e6f933a5c56e07b18ef70f44949f34bf57f5d5318e2199c12cbfbc"}, + {file = "glcontext-2.3.7-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4d18b3e9e9259595dd5c538c1fd9238f8b26c22d6351397e721ef8a89ad55f12"}, + {file = "glcontext-2.3.7-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:376e12d230fd198a329dfe253b41480b0a015a2dabbac5eecf6b279fe3afb1b3"}, + {file = "glcontext-2.3.7-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:84dc3b831af386cb20cae8fb10ac78d8007bb29118730db2e9f21c329a528028"}, + {file = "glcontext-2.3.7-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b85c873315741dbc208c199cbe449aa77d1831551dd78d9b3d67e0a6f9eb576d"}, + {file = "glcontext-2.3.7-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:94e47dd8cf39cabe20b41dd0c4c6589f0c7a4de2a5bad8e51ab0fc0b4a26ae6b"}, + {file = "glcontext-2.3.7-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:79e561b67e606b6e13ba58e6ae3e688e3429dbb5d60e551ba40d649432044f37"}, + {file = "glcontext-2.3.7-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:d986976c9b758d60d966fbaf8bdff129d125e8b2c58889d2220ca96991f1071e"}, + {file = "glcontext-2.3.7-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:891b56a3bbaf3470595c218e847e79448e95cecb412224c8585da640c61cf29a"}, + {file = "glcontext-2.3.7-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a349317c9d634aa56e30aae9ad408bc1b9de281af0e4f87de682b454ebaf540e"}, + {file = "glcontext-2.3.7-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1716d21d423a1a2261cd717bc66714eeb5464d6a061b92678f356ca69cfd1255"}, + {file = "glcontext-2.3.7-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:440ff5f59f318ce495c6bdddfa01a23dd64713fb960ceb87c3a9423745781d47"}, + {file = "glcontext-2.3.7-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:ef0c7e534e53f14b7b09dc3fe1e207243c9bb3eb2543d9876ed253156ca7a8bf"}, + {file = "glcontext-2.3.7.tar.gz", hash = "sha256:bb2d0503f45ad85ca7319bd37fd983e374b3f824c38a450b5f72cfc974114156"}, +] + +[[package]] +name = "glfw" +version = "1.12.0" +description = "A ctypes-based wrapper for GLFW3." +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "glfw-1.12.0-py2.py27.py3.py30.py31.py32.py33.py34.py35.py36.py37.py38-none-macosx_10_6_intel.whl", hash = "sha256:88bd1cd2ace036d275e9af8312993068d94b3ac19248421eedc35a4baf53bb8c"}, + {file = "glfw-1.12.0-py2.py27.py3.py30.py31.py32.py33.py34.py35.py36.py37.py38-none-manylinux2010_i686.whl", hash = "sha256:bb63f6121c40f5f17cd78328c040b40aeaca9ed748f440c40d2fcad824107a74"}, + {file = "glfw-1.12.0-py2.py27.py3.py30.py31.py32.py33.py34.py35.py36.py37.py38-none-manylinux2010_x86_64.whl", hash = "sha256:6324fed2c4fd1762ae580277c534b5dab6f360b8fb9aed3e1547cf33f63d207c"}, + {file = "glfw-1.12.0-py2.py27.py3.py30.py31.py32.py33.py34.py35.py36.py37.py38-none-manylinux2014_x86_64.whl", hash = "sha256:20c918a1ae413a009f7559be9559bc2ec1d6251888f588ffa7a174d6db69a942"}, + {file = "glfw-1.12.0-py2.py27.py3.py30.py31.py32.py33.py34.py35.py36.py37.py38-none-win32.whl", hash = "sha256:fe9622a48ec9dc436e67de0d0bb4c9443996b5bd5564df1734d3a4280b728d38"}, + {file = "glfw-1.12.0-py2.py27.py3.py30.py31.py32.py33.py34.py35.py36.py37.py38-none-win_amd64.whl", hash = "sha256:3723db5e5628b2cd8729421fb6fc5fed3e5d25c7d1631f72f13301b218ee1600"}, + {file = "glfw-1.12.0.tar.gz", hash = "sha256:f195ed7a43475e4f1603903d6999f3a6b470fda88bd1749ff10adc520abe8fb1"}, +] + +[[package]] +name = "google-auth" +version = "2.16.0" +description = "Google Authentication Library" +category = "main" +optional = false +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*" +files = [ + {file = "google-auth-2.16.0.tar.gz", hash = "sha256:ed7057a101af1146f0554a769930ac9de506aeca4fd5af6543ebe791851a9fbd"}, + {file = "google_auth-2.16.0-py2.py3-none-any.whl", hash = "sha256:5045648c821fb72384cdc0e82cc326df195f113a33049d9b62b74589243d2acc"}, +] + +[package.dependencies] +cachetools = ">=2.0.0,<6.0" +pyasn1-modules = ">=0.2.1" +rsa = {version = ">=3.1.4,<5", markers = "python_version >= \"3.6\""} +six = ">=1.9.0" + +[package.extras] +aiohttp = ["aiohttp (>=3.6.2,<4.0.0dev)", "requests (>=2.20.0,<3.0.0dev)"] +enterprise-cert = ["cryptography (==36.0.2)", "pyopenssl (==22.0.0)"] +pyopenssl = ["cryptography (>=38.0.3)", "pyopenssl (>=20.0.0)"] +reauth = ["pyu2f (>=0.1.5)"] +requests = ["requests (>=2.20.0,<3.0.0dev)"] + +[[package]] +name = "google-auth-oauthlib" +version = "0.4.6" +description = "Google Authentication Library" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "google-auth-oauthlib-0.4.6.tar.gz", hash = "sha256:a90a072f6993f2c327067bf65270046384cda5a8ecb20b94ea9a687f1f233a7a"}, + {file = "google_auth_oauthlib-0.4.6-py2.py3-none-any.whl", hash = "sha256:3f2a6e802eebbb6fb736a370fbf3b055edcb6b52878bf2f26330b5e041316c73"}, +] + +[package.dependencies] +google-auth = ">=1.0.0" +requests-oauthlib = ">=0.7.0" + +[package.extras] +tool = ["click (>=6.0.0)"] + +[[package]] +name = "grpcio" +version = "1.51.1" +description = "HTTP/2-based RPC framework" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "grpcio-1.51.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:cc2bece1737b44d878cc1510ea04469a8073dbbcdd762175168937ae4742dfb3"}, + {file = "grpcio-1.51.1-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:e223a9793522680beae44671b9ed8f6d25bbe5ddf8887e66aebad5e0686049ef"}, + {file = "grpcio-1.51.1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:24ac1154c4b2ab4a0c5326a76161547e70664cd2c39ba75f00fc8a2170964ea2"}, + {file = "grpcio-1.51.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e4ef09f8997c4be5f3504cefa6b5c6cc3cf648274ce3cede84d4342a35d76db6"}, + {file = "grpcio-1.51.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8a0b77e992c64880e6efbe0086fe54dfc0bbd56f72a92d9e48264dcd2a3db98"}, + {file = "grpcio-1.51.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:eacad297ea60c72dd280d3353d93fb1dcca952ec11de6bb3c49d12a572ba31dd"}, + {file = "grpcio-1.51.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:16c71740640ba3a882f50b01bf58154681d44b51f09a5728180a8fdc66c67bd5"}, + {file = "grpcio-1.51.1-cp310-cp310-win32.whl", hash = "sha256:29cb97d41a4ead83b7bcad23bdb25bdd170b1e2cba16db6d3acbb090bc2de43c"}, + {file = "grpcio-1.51.1-cp310-cp310-win_amd64.whl", hash = "sha256:9ff42c5620b4e4530609e11afefa4a62ca91fa0abb045a8957e509ef84e54d30"}, + {file = "grpcio-1.51.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:bc59f7ba87972ab236f8669d8ca7400f02a0eadf273ca00e02af64d588046f02"}, + {file = "grpcio-1.51.1-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:3c2b3842dcf870912da31a503454a33a697392f60c5e2697c91d133130c2c85d"}, + {file = "grpcio-1.51.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22b011674090594f1f3245960ced7386f6af35485a38901f8afee8ad01541dbd"}, + {file = "grpcio-1.51.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49d680356a975d9c66a678eb2dde192d5dc427a7994fb977363634e781614f7c"}, + {file = "grpcio-1.51.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:094e64236253590d9d4075665c77b329d707b6fca864dd62b144255e199b4f87"}, + {file = "grpcio-1.51.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:257478300735ce3c98d65a930bbda3db172bd4e00968ba743e6a1154ea6edf10"}, + {file = "grpcio-1.51.1-cp311-cp311-win32.whl", hash = "sha256:5a6ebcdef0ef12005d56d38be30f5156d1cb3373b52e96f147f4a24b0ddb3a9d"}, + {file = "grpcio-1.51.1-cp311-cp311-win_amd64.whl", hash = "sha256:3f9b0023c2c92bebd1be72cdfca23004ea748be1813a66d684d49d67d836adde"}, + {file = "grpcio-1.51.1-cp37-cp37m-linux_armv7l.whl", hash = "sha256:cd3baccea2bc5c38aeb14e5b00167bd4e2373a373a5e4d8d850bd193edad150c"}, + {file = "grpcio-1.51.1-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:17ec9b13cec4a286b9e606b48191e560ca2f3bbdf3986f91e480a95d1582e1a7"}, + {file = "grpcio-1.51.1-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:fbdbe9a849854fe484c00823f45b7baab159bdd4a46075302281998cb8719df5"}, + {file = "grpcio-1.51.1-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:31bb6bc7ff145e2771c9baf612f4b9ebbc9605ccdc5f3ff3d5553de7fc0e0d79"}, + {file = "grpcio-1.51.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e473525c28251558337b5c1ad3fa969511e42304524a4e404065e165b084c9e4"}, + {file = "grpcio-1.51.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:6f0b89967ee11f2b654c23b27086d88ad7bf08c0b3c2a280362f28c3698b2896"}, + {file = "grpcio-1.51.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:7942b32a291421460d6a07883033e392167d30724aa84987e6956cd15f1a21b9"}, + {file = "grpcio-1.51.1-cp37-cp37m-win32.whl", hash = "sha256:f96ace1540223f26fbe7c4ebbf8a98e3929a6aa0290c8033d12526847b291c0f"}, + {file = "grpcio-1.51.1-cp37-cp37m-win_amd64.whl", hash = "sha256:f1fec3abaf274cdb85bf3878167cfde5ad4a4d97c68421afda95174de85ba813"}, + {file = "grpcio-1.51.1-cp38-cp38-linux_armv7l.whl", hash = "sha256:0e1a9e1b4a23808f1132aa35f968cd8e659f60af3ffd6fb00bcf9a65e7db279f"}, + {file = "grpcio-1.51.1-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:6df3b63538c362312bc5fa95fb965069c65c3ea91d7ce78ad9c47cab57226f54"}, + {file = "grpcio-1.51.1-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:172405ca6bdfedd6054c74c62085946e45ad4d9cec9f3c42b4c9a02546c4c7e9"}, + {file = "grpcio-1.51.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:506b9b7a4cede87d7219bfb31014d7b471cfc77157da9e820a737ec1ea4b0663"}, + {file = "grpcio-1.51.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0fb93051331acbb75b49a2a0fd9239c6ba9528f6bdc1dd400ad1cb66cf864292"}, + {file = "grpcio-1.51.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5dca372268c6ab6372d37d6b9f9343e7e5b4bc09779f819f9470cd88b2ece3c3"}, + {file = "grpcio-1.51.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:471d39d3370ca923a316d49c8aac66356cea708a11e647e3bdc3d0b5de4f0a40"}, + {file = "grpcio-1.51.1-cp38-cp38-win32.whl", hash = "sha256:75e29a90dc319f0ad4d87ba6d20083615a00d8276b51512e04ad7452b5c23b04"}, + {file = "grpcio-1.51.1-cp38-cp38-win_amd64.whl", hash = "sha256:f1158bccbb919da42544a4d3af5d9296a3358539ffa01018307337365a9a0c64"}, + {file = "grpcio-1.51.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:59dffade859f157bcc55243714d57b286da6ae16469bf1ac0614d281b5f49b67"}, + {file = "grpcio-1.51.1-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:dad6533411d033b77f5369eafe87af8583178efd4039c41d7515d3336c53b4f1"}, + {file = "grpcio-1.51.1-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:4c4423ea38a7825b8fed8934d6d9aeebdf646c97e3c608c3b0bcf23616f33877"}, + {file = "grpcio-1.51.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0dc5354e38e5adf2498312f7241b14c7ce3484eefa0082db4297189dcbe272e6"}, + {file = "grpcio-1.51.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97d67983189e2e45550eac194d6234fc38b8c3b5396c153821f2d906ed46e0ce"}, + {file = "grpcio-1.51.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:538d981818e49b6ed1e9c8d5e5adf29f71c4e334e7d459bf47e9b7abb3c30e09"}, + {file = "grpcio-1.51.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9235dcd5144a83f9ca6f431bd0eccc46b90e2c22fe27b7f7d77cabb2fb515595"}, + {file = "grpcio-1.51.1-cp39-cp39-win32.whl", hash = "sha256:aacb54f7789ede5cbf1d007637f792d3e87f1c9841f57dd51abf89337d1b8472"}, + {file = "grpcio-1.51.1-cp39-cp39-win_amd64.whl", hash = "sha256:2b170eaf51518275c9b6b22ccb59450537c5a8555326fd96ff7391b5dd75303c"}, + {file = "grpcio-1.51.1.tar.gz", hash = "sha256:e6dfc2b6567b1c261739b43d9c59d201c1b89e017afd9e684d85aa7a186c9f7a"}, +] + +[package.extras] +protobuf = ["grpcio-tools (>=1.51.1)"] + +[[package]] +name = "gym" +version = "0.21.0" +description = "Gym: A universal API for reinforcement learning environments." +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "gym-0.21.0.tar.gz", hash = "sha256:0fd1ce165c754b4017e37a617b097c032b8c3feb8a0394ccc8777c7c50dddff3"}, +] + +[package.dependencies] +box2d-py = {version = "2.3.5", optional = true, markers = "extra == \"box2d\""} +cloudpickle = ">=1.2.0" +numpy = ">=1.18.0" +pyglet = {version = ">=1.4.0", optional = true, markers = "extra == \"box2d\""} + +[package.extras] +accept-rom-license = ["autorom[accept-rom-license] (>=0.4.2,<0.5.0)"] +all = ["ale-py (>=0.7.1,<0.8.0)", "ale-py (>=0.7.1,<0.8.0)", "box2d-py (==2.3.5)", "box2d-py (==2.3.5)", "lz4 (>=3.1.0)", "lz4 (>=3.1.0)", "mujoco_py (>=1.50,<2.0)", "mujoco_py (>=1.50,<2.0)", "pyglet (>=1.4.0)", "pyglet (>=1.4.0)", "pyglet (>=1.4.0)", "pyglet (>=1.4.0)", "scipy (>=1.4.1)", "scipy (>=1.4.1)"] +atari = ["ale-py (>=0.7.1,<0.8.0)"] +box2d = ["box2d-py (==2.3.5)", "pyglet (>=1.4.0)"] +classic-control = ["pyglet (>=1.4.0)"] +mujoco = ["mujoco_py (>=1.50,<2.0)"] +nomujoco = ["ale-py (>=0.7.1,<0.8.0)", "box2d-py (==2.3.5)", "lz4 (>=3.1.0)", "pyglet (>=1.4.0)", "pyglet (>=1.4.0)", "scipy (>=1.4.1)"] +other = ["lz4 (>=3.1.0)"] +robotics = ["mujoco_py (>=1.50,<2.0)"] +toy-text = ["scipy (>=1.4.1)"] + +[[package]] +name = "gym3" +version = "0.3.3" +description = "Vectorized Reinforcement Learning Environment Interface" +category = "main" +optional = false +python-versions = ">=3.6.0" +files = [ + {file = "gym3-0.3.3-py3-none-any.whl", hash = "sha256:bacc0e124f8ce5e1d8a9dceb85725123332954f13ef4e226133506597548838a"}, +] + +[package.dependencies] +cffi = ">=1.13.0,<2.0.0" +glfw = ">=1.8.6,<2.0.0" +imageio = ">=2.6.0,<3.0.0" +imageio-ffmpeg = ">=0.3.0,<0.4.0" +moderngl = ">=5.5.4,<6.0.0" +numpy = ">=1.11.0,<2.0.0" + +[package.extras] +test = ["gym (==0.17.2)", "gym-retro (==0.8.0)", "mpi4py (==3.0.3)", "pytest (==5.2.1)", "pytest-benchmark (==3.2.2)", "tensorflow (==1.15.0)"] + +[[package]] +name = "html5lib" +version = "1.1" +description = "HTML parser based on the WHATWG HTML specification" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "html5lib-1.1-py2.py3-none-any.whl", hash = "sha256:0d78f8fde1c230e99fe37986a60526d7049ed4bf8a9fadbad5f00e22e58e041d"}, + {file = "html5lib-1.1.tar.gz", hash = "sha256:b2e5b40261e20f354d198eae92afc10d750afb487ed5e50f9c4eaf07c184146f"}, +] + +[package.dependencies] +six = ">=1.9" +webencodings = "*" + +[package.extras] +all = ["chardet (>=2.2)", "genshi", "lxml"] +chardet = ["chardet (>=2.2)"] +genshi = ["genshi"] +lxml = ["lxml"] + +[[package]] +name = "huggingface-hub" +version = "0.12.0" +description = "Client library to download and publish models, datasets and other repos on the huggingface.co hub" +category = "main" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "huggingface_hub-0.12.0-py3-none-any.whl", hash = "sha256:93809eabbfb2058a808bddf8b2a70f645de3f9df73ce87ddf5163d4c74b71c0c"}, + {file = "huggingface_hub-0.12.0.tar.gz", hash = "sha256:da82c9ec8f9d8f976ffd3fd8249d20bb35c2dd3145a9f7ca1106f0ebefd9afa0"}, +] + +[package.dependencies] +filelock = "*" +packaging = ">=20.9" +pyyaml = ">=5.1" +requests = "*" +tqdm = ">=4.42.1" +typing-extensions = ">=3.7.4.3" + +[package.extras] +all = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "black (==22.3)", "flake8 (>=3.8.3)", "flake8-bugbear", "isort (>=5.5.4)", "jedi", "mypy (==0.982)", "pytest", "pytest-cov", "pytest-env", "pytest-xdist", "soundfile", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3"] +cli = ["InquirerPy (==0.3.4)"] +dev = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "black (==22.3)", "flake8 (>=3.8.3)", "flake8-bugbear", "isort (>=5.5.4)", "jedi", "mypy (==0.982)", "pytest", "pytest-cov", "pytest-env", "pytest-xdist", "soundfile", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3"] +fastai = ["fastai (>=2.4)", "fastcore (>=1.3.27)", "toml"] +quality = ["black (==22.3)", "flake8 (>=3.8.3)", "flake8-bugbear", "isort (>=5.5.4)", "mypy (==0.982)"] +tensorflow = ["graphviz", "pydot", "tensorflow"] +testing = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "isort (>=5.5.4)", "jedi", "pytest", "pytest-cov", "pytest-env", "pytest-xdist", "soundfile"] +torch = ["torch"] +typing = ["types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3"] + +[[package]] +name = "idna" +version = "3.4" +description = "Internationalized Domain Names in Applications (IDNA)" +category = "main" +optional = false +python-versions = ">=3.5" +files = [ + {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, + {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, +] + +[[package]] +name = "imageio" +version = "2.25.0" +description = "Library for reading and writing a wide range of image, video, scientific, and volumetric data formats." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "imageio-2.25.0-py3-none-any.whl", hash = "sha256:9ef2fdef1235eef849b1aea399f08508493624a2a2c8cc3bba957dabb7d0b79f"}, + {file = "imageio-2.25.0.tar.gz", hash = "sha256:b80796a1f8c38c697a940a2ad7397ee28900d5c4e51061b9a67d16aca867f33e"}, +] + +[package.dependencies] +numpy = "*" +pillow = ">=8.3.2" + +[package.extras] +all-plugins = ["astropy", "av", "imageio-ffmpeg", "opencv-python", "psutil", "tifffile"] +all-plugins-pypy = ["av", "imageio-ffmpeg", "psutil", "tifffile"] +build = ["wheel"] +dev = ["black", "flake8", "fsspec[github]", "invoke", "pytest", "pytest-cov"] +docs = ["numpydoc", "pydata-sphinx-theme", "sphinx (<6)"] +ffmpeg = ["imageio-ffmpeg", "psutil"] +fits = ["astropy"] +full = ["astropy", "av", "black", "flake8", "fsspec[github]", "gdal", "imageio-ffmpeg", "invoke", "itk", "numpydoc", "opencv-python", "psutil", "pydata-sphinx-theme", "pytest", "pytest-cov", "sphinx (<6)", "tifffile", "wheel"] +gdal = ["gdal"] +itk = ["itk"] +linting = ["black", "flake8"] +opencv = ["opencv-python"] +pyav = ["av"] +test = ["fsspec[github]", "invoke", "pytest", "pytest-cov"] +tifffile = ["tifffile"] + +[[package]] +name = "imageio-ffmpeg" +version = "0.3.0" +description = "FFMPEG wrapper for Python" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "imageio-ffmpeg-0.3.0.tar.gz", hash = "sha256:28500544acdebc195159d53a4670b76d596f368b218317bad5d3cbf43b00d6c2"}, + {file = "imageio_ffmpeg-0.3.0-py3-none-macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:f99476aa42aac2ca0657483b417874a825b2f526aaa8a7a823b8a803f366caab"}, + {file = "imageio_ffmpeg-0.3.0-py3-none-manylinux2010_x86_64.whl", hash = "sha256:f3d4096bf6e211540c4f6b9628c56d8e700cf12027ed842724173ab9c6666d1a"}, + {file = "imageio_ffmpeg-0.3.0-py3-none-win32.whl", hash = "sha256:400345dd194f2cb2b424294aa0f3c90afce1de96879ffe3266afeece3494d93c"}, + {file = "imageio_ffmpeg-0.3.0-py3-none-win_amd64.whl", hash = "sha256:991416c0eed0d221229e67342b8264a8b9defdec61d8a9e7688b90dbb838fb1e"}, +] + +[[package]] +name = "importlib-metadata" +version = "4.13.0" +description = "Read metadata from Python packages" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "importlib_metadata-4.13.0-py3-none-any.whl", hash = "sha256:8a8a81bcf996e74fee46f0d16bd3eaa382a7eb20fd82445c3ad11f4090334116"}, + {file = "importlib_metadata-4.13.0.tar.gz", hash = "sha256:dd0173e8f150d6815e098fd354f6414b0f079af4644ddfe90c71e2fc6174346d"}, +] + +[package.dependencies] +zipp = ">=0.5" + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"] +perf = ["ipython"] +testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"] + +[[package]] +name = "importlib-resources" +version = "5.10.2" +description = "Read resources from Python packages" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "importlib_resources-5.10.2-py3-none-any.whl", hash = "sha256:7d543798b0beca10b6a01ac7cafda9f822c54db9e8376a6bf57e0cbd74d486b6"}, + {file = "importlib_resources-5.10.2.tar.gz", hash = "sha256:e4a96c8cc0339647ff9a5e0550d9f276fc5a01ffa276012b58ec108cfd7b8484"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["flake8 (<5)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] + +[[package]] +name = "ipykernel" +version = "6.20.2" +description = "IPython Kernel for Jupyter" +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "ipykernel-6.20.2-py3-none-any.whl", hash = "sha256:5d0675d5f48bf6a95fd517d7b70bcb3b2c5631b2069949b5c2d6e1d7477fb5a0"}, + {file = "ipykernel-6.20.2.tar.gz", hash = "sha256:1893c5b847033cd7a58f6843b04a9349ffb1031bc6588401cadc9adb58da428e"}, +] + +[package.dependencies] +appnope = {version = "*", markers = "platform_system == \"Darwin\""} +comm = ">=0.1.1" +debugpy = ">=1.0" +ipython = ">=7.23.1" +jupyter-client = ">=6.1.12" +matplotlib-inline = ">=0.1" +nest-asyncio = "*" +packaging = "*" +psutil = "*" +pyzmq = ">=17" +tornado = ">=6.1" +traitlets = ">=5.4.0" + +[package.extras] +cov = ["coverage[toml]", "curio", "matplotlib", "pytest-cov", "trio"] +docs = ["myst-parser", "pydata-sphinx-theme", "sphinx", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-spelling", "trio"] +pyqt5 = ["pyqt5"] +pyside6 = ["pyside6"] +test = ["flaky", "ipyparallel", "pre-commit", "pytest (>=7.0)", "pytest-asyncio", "pytest-cov", "pytest-timeout"] + +[[package]] +name = "ipython" +version = "8.10.0" +description = "IPython: Productive Interactive Computing" +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "ipython-8.10.0-py3-none-any.whl", hash = "sha256:b38c31e8fc7eff642fc7c597061fff462537cf2314e3225a19c906b7b0d8a345"}, + {file = "ipython-8.10.0.tar.gz", hash = "sha256:b13a1d6c1f5818bd388db53b7107d17454129a70de2b87481d555daede5eb49e"}, +] + +[package.dependencies] +appnope = {version = "*", markers = "sys_platform == \"darwin\""} +backcall = "*" +colorama = {version = "*", markers = "sys_platform == \"win32\""} +decorator = "*" +jedi = ">=0.16" +matplotlib-inline = "*" +pexpect = {version = ">4.3", markers = "sys_platform != \"win32\""} +pickleshare = "*" +prompt-toolkit = ">=3.0.30,<3.1.0" +pygments = ">=2.4.0" +stack-data = "*" +traitlets = ">=5" + +[package.extras] +all = ["black", "curio", "docrepr", "ipykernel", "ipyparallel", "ipywidgets", "matplotlib", "matplotlib (!=3.2.0)", "nbconvert", "nbformat", "notebook", "numpy (>=1.21)", "pandas", "pytest (<7)", "pytest (<7.1)", "pytest-asyncio", "qtconsole", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "stack-data", "testpath", "trio", "typing-extensions"] +black = ["black"] +doc = ["docrepr", "ipykernel", "matplotlib", "pytest (<7)", "pytest (<7.1)", "pytest-asyncio", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "stack-data", "testpath", "typing-extensions"] +kernel = ["ipykernel"] +nbconvert = ["nbconvert"] +nbformat = ["nbformat"] +notebook = ["ipywidgets", "notebook"] +parallel = ["ipyparallel"] +qtconsole = ["qtconsole"] +test = ["pytest (<7.1)", "pytest-asyncio", "testpath"] +test-extra = ["curio", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.21)", "pandas", "pytest (<7.1)", "pytest-asyncio", "testpath", "trio"] + +[[package]] +name = "ipython-genutils" +version = "0.2.0" +description = "Vestigial utilities from IPython" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "ipython_genutils-0.2.0-py2.py3-none-any.whl", hash = "sha256:72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8"}, + {file = "ipython_genutils-0.2.0.tar.gz", hash = "sha256:eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8"}, +] + +[[package]] +name = "ipywidgets" +version = "8.0.4" +description = "Jupyter interactive widgets" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "ipywidgets-8.0.4-py3-none-any.whl", hash = "sha256:ebb195e743b16c3947fe8827190fb87b4d00979c0fbf685afe4d2c4927059fa1"}, + {file = "ipywidgets-8.0.4.tar.gz", hash = "sha256:c0005a77a47d77889cafed892b58e33b4a2a96712154404c6548ec22272811ea"}, +] + +[package.dependencies] +ipykernel = ">=4.5.1" +ipython = ">=6.1.0" +jupyterlab-widgets = ">=3.0,<4.0" +traitlets = ">=4.3.1" +widgetsnbextension = ">=4.0,<5.0" + +[package.extras] +test = ["jsonschema", "pytest (>=3.6.0)", "pytest-cov", "pytz"] + +[[package]] +name = "isoduration" +version = "20.11.0" +description = "Operations with ISO 8601 durations" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "isoduration-20.11.0-py3-none-any.whl", hash = "sha256:b2904c2a4228c3d44f409c8ae8e2370eb21a26f7ac2ec5446df141dde3452042"}, + {file = "isoduration-20.11.0.tar.gz", hash = "sha256:ac2f9015137935279eac671f94f89eb00584f940f5dc49462a0c4ee692ba1bd9"}, +] + +[package.dependencies] +arrow = ">=0.15.0" + +[[package]] +name = "jaraco-classes" +version = "3.2.3" +description = "Utility functions for Python class constructs" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "jaraco.classes-3.2.3-py3-none-any.whl", hash = "sha256:2353de3288bc6b82120752201c6b1c1a14b058267fa424ed5ce5984e3b922158"}, + {file = "jaraco.classes-3.2.3.tar.gz", hash = "sha256:89559fa5c1d3c34eff6f631ad80bb21f378dbcbb35dd161fd2c6b93f5be2f98a"}, +] + +[package.dependencies] +more-itertools = "*" + +[package.extras] +docs = ["jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"] +testing = ["flake8 (<5)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] + +[[package]] +name = "jedi" +version = "0.18.2" +description = "An autocompletion tool for Python that can be used for text editors." +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "jedi-0.18.2-py2.py3-none-any.whl", hash = "sha256:203c1fd9d969ab8f2119ec0a3342e0b49910045abe6af0a3ae83a5764d54639e"}, + {file = "jedi-0.18.2.tar.gz", hash = "sha256:bae794c30d07f6d910d32a7048af09b5a39ed740918da923c6b780790ebac612"}, +] + +[package.dependencies] +parso = ">=0.8.0,<0.9.0" + +[package.extras] +docs = ["Jinja2 (==2.11.3)", "MarkupSafe (==1.1.1)", "Pygments (==2.8.1)", "alabaster (==0.7.12)", "babel (==2.9.1)", "chardet (==4.0.0)", "commonmark (==0.8.1)", "docutils (==0.17.1)", "future (==0.18.2)", "idna (==2.10)", "imagesize (==1.2.0)", "mock (==1.0.1)", "packaging (==20.9)", "pyparsing (==2.4.7)", "pytz (==2021.1)", "readthedocs-sphinx-ext (==2.1.4)", "recommonmark (==0.5.0)", "requests (==2.25.1)", "six (==1.15.0)", "snowballstemmer (==2.1.0)", "sphinx (==1.8.5)", "sphinx-rtd-theme (==0.4.3)", "sphinxcontrib-serializinghtml (==1.1.4)", "sphinxcontrib-websupport (==1.2.4)", "urllib3 (==1.26.4)"] +qa = ["flake8 (==3.8.3)", "mypy (==0.782)"] +testing = ["Django (<3.1)", "attrs", "colorama", "docopt", "pytest (<7.0.0)"] + +[[package]] +name = "jeepney" +version = "0.8.0" +description = "Low-level, pure Python DBus protocol wrapper." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "jeepney-0.8.0-py3-none-any.whl", hash = "sha256:c0a454ad016ca575060802ee4d590dd912e35c122fa04e70306de3d076cce755"}, + {file = "jeepney-0.8.0.tar.gz", hash = "sha256:5efe48d255973902f6badc3ce55e2aa6c5c3b3bc642059ef3a91247bcfcc5806"}, +] + +[package.extras] +test = ["async-timeout", "pytest", "pytest-asyncio (>=0.17)", "pytest-trio", "testpath", "trio"] +trio = ["async_generator", "trio"] + +[[package]] +name = "jinja2" +version = "3.1.2" +description = "A very fast and expressive template engine." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, + {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, +] + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "jsonpointer" +version = "2.3" +description = "Identify specific nodes in a JSON document (RFC 6901)" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "jsonpointer-2.3-py2.py3-none-any.whl", hash = "sha256:51801e558539b4e9cd268638c078c6c5746c9ac96bc38152d443400e4f3793e9"}, + {file = "jsonpointer-2.3.tar.gz", hash = "sha256:97cba51526c829282218feb99dab1b1e6bdf8efd1c43dc9d57be093c0d69c99a"}, +] + +[[package]] +name = "jsonschema" +version = "4.17.3" +description = "An implementation of JSON Schema validation for Python" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "jsonschema-4.17.3-py3-none-any.whl", hash = "sha256:a870ad254da1a8ca84b6a2905cac29d265f805acc57af304784962a2aa6508f6"}, + {file = "jsonschema-4.17.3.tar.gz", hash = "sha256:0f864437ab8b6076ba6707453ef8f98a6a0d512a80e93f8abdb676f737ecb60d"}, +] + +[package.dependencies] +attrs = ">=17.4.0" +fqdn = {version = "*", optional = true, markers = "extra == \"format-nongpl\""} +idna = {version = "*", optional = true, markers = "extra == \"format-nongpl\""} +isoduration = {version = "*", optional = true, markers = "extra == \"format-nongpl\""} +jsonpointer = {version = ">1.13", optional = true, markers = "extra == \"format-nongpl\""} +pyrsistent = ">=0.14.0,<0.17.0 || >0.17.0,<0.17.1 || >0.17.1,<0.17.2 || >0.17.2" +rfc3339-validator = {version = "*", optional = true, markers = "extra == \"format-nongpl\""} +rfc3986-validator = {version = ">0.1.0", optional = true, markers = "extra == \"format-nongpl\""} +uri-template = {version = "*", optional = true, markers = "extra == \"format-nongpl\""} +webcolors = {version = ">=1.11", optional = true, markers = "extra == \"format-nongpl\""} + +[package.extras] +format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] +format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=1.11)"] + +[[package]] +name = "jupyter" +version = "1.0.0" +description = "Jupyter metapackage. Install all the Jupyter components in one go." +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "jupyter-1.0.0-py2.py3-none-any.whl", hash = "sha256:5b290f93b98ffbc21c0c7e749f054b3267782166d72fa5e3ed1ed4eaf34a2b78"}, + {file = "jupyter-1.0.0.tar.gz", hash = "sha256:d9dc4b3318f310e34c82951ea5d6683f67bed7def4b259fafbfe4f1beb1d8e5f"}, + {file = "jupyter-1.0.0.zip", hash = "sha256:3e1f86076bbb7c8c207829390305a2b1fe836d471ed54be66a3b8c41e7f46cc7"}, +] + +[package.dependencies] +ipykernel = "*" +ipywidgets = "*" +jupyter-console = "*" +nbconvert = "*" +notebook = "*" +qtconsole = "*" + +[[package]] +name = "jupyter-client" +version = "7.4.9" +description = "Jupyter protocol implementation and client libraries" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "jupyter_client-7.4.9-py3-none-any.whl", hash = "sha256:214668aaea208195f4c13d28eb272ba79f945fc0cf3f11c7092c20b2ca1980e7"}, + {file = "jupyter_client-7.4.9.tar.gz", hash = "sha256:52be28e04171f07aed8f20e1616a5a552ab9fee9cbbe6c1896ae170c3880d392"}, +] + +[package.dependencies] +entrypoints = "*" +jupyter-core = ">=4.9.2" +nest-asyncio = ">=1.5.4" +python-dateutil = ">=2.8.2" +pyzmq = ">=23.0" +tornado = ">=6.2" +traitlets = "*" + +[package.extras] +doc = ["ipykernel", "myst-parser", "sphinx (>=1.3.6)", "sphinx-rtd-theme", "sphinxcontrib-github-alt"] +test = ["codecov", "coverage", "ipykernel (>=6.12)", "ipython", "mypy", "pre-commit", "pytest", "pytest-asyncio (>=0.18)", "pytest-cov", "pytest-timeout"] + +[[package]] +name = "jupyter-console" +version = "6.4.4" +description = "Jupyter terminal console" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "jupyter_console-6.4.4-py3-none-any.whl", hash = "sha256:756df7f4f60c986e7bc0172e4493d3830a7e6e75c08750bbe59c0a5403ad6dee"}, + {file = "jupyter_console-6.4.4.tar.gz", hash = "sha256:172f5335e31d600df61613a97b7f0352f2c8250bbd1092ef2d658f77249f89fb"}, +] + +[package.dependencies] +ipykernel = "*" +ipython = "*" +jupyter-client = ">=7.0.0" +prompt-toolkit = ">=2.0.0,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.1.0" +pygments = "*" + +[package.extras] +test = ["pexpect"] + +[[package]] +name = "jupyter-core" +version = "5.1.3" +description = "Jupyter core package. A base package on which Jupyter projects rely." +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "jupyter_core-5.1.3-py3-none-any.whl", hash = "sha256:d23ab7db81ca1759f13780cd6b65f37f59bf8e0186ac422d5ca4982cc7d56716"}, + {file = "jupyter_core-5.1.3.tar.gz", hash = "sha256:82e1cff0ef804c38677eff7070d5ff1d45037fef01a2d9ba9e6b7b8201831e9f"}, +] + +[package.dependencies] +platformdirs = ">=2.5" +pywin32 = {version = ">=1.0", markers = "sys_platform == \"win32\" and platform_python_implementation != \"PyPy\""} +traitlets = ">=5.3" + +[package.extras] +docs = ["myst-parser", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-spelling", "traitlets"] +test = ["ipykernel", "pre-commit", "pytest", "pytest-cov", "pytest-timeout"] + +[[package]] +name = "jupyter-events" +version = "0.6.3" +description = "Jupyter Event System library" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "jupyter_events-0.6.3-py3-none-any.whl", hash = "sha256:57a2749f87ba387cd1bfd9b22a0875b889237dbf2edc2121ebb22bde47036c17"}, + {file = "jupyter_events-0.6.3.tar.gz", hash = "sha256:9a6e9995f75d1b7146b436ea24d696ce3a35bfa8bfe45e0c33c334c79464d0b3"}, +] + +[package.dependencies] +jsonschema = {version = ">=3.2.0", extras = ["format-nongpl"]} +python-json-logger = ">=2.0.4" +pyyaml = ">=5.3" +rfc3339-validator = "*" +rfc3986-validator = ">=0.1.1" +traitlets = ">=5.3" + +[package.extras] +cli = ["click", "rich"] +docs = ["jupyterlite-sphinx", "myst-parser", "pydata-sphinx-theme", "sphinxcontrib-spelling"] +test = ["click", "coverage", "pre-commit", "pytest (>=7.0)", "pytest-asyncio (>=0.19.0)", "pytest-console-scripts", "pytest-cov", "rich"] + +[[package]] +name = "jupyter-server" +version = "2.1.0" +description = "The backend—i.e. core services, APIs, and REST endpoints—to Jupyter web applications." +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "jupyter_server-2.1.0-py3-none-any.whl", hash = "sha256:90cd6f2bd0581ddd9b2dbe82026a0f4c228a1d95c86e22460efbfdfc931fcf56"}, + {file = "jupyter_server-2.1.0.tar.gz", hash = "sha256:efaae5e4f0d5f22c7f2f2dc848635036ee74a2df02abed52d30d9d95121ad382"}, +] + +[package.dependencies] +anyio = ">=3.1.0,<4" +argon2-cffi = "*" +jinja2 = "*" +jupyter-client = ">=7.4.4" +jupyter-core = ">=4.12,<5.0.0 || >=5.1.0" +jupyter-events = ">=0.4.0" +jupyter-server-terminals = "*" +nbconvert = ">=6.4.4" +nbformat = ">=5.3.0" +packaging = "*" +prometheus-client = "*" +pywinpty = {version = "*", markers = "os_name == \"nt\""} +pyzmq = ">=24" +send2trash = "*" +terminado = ">=0.8.3" +tornado = ">=6.2.0" +traitlets = ">=5.6.0" +websocket-client = "*" + +[package.extras] +docs = ["docutils (<0.20)", "ipykernel", "jinja2", "jupyter-client", "jupyter-server", "mistune (<1.0.0)", "myst-parser", "nbformat", "prometheus-client", "pydata-sphinx-theme", "send2trash", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-openapi", "sphinxcontrib-spelling", "sphinxemoji", "tornado"] +test = ["ipykernel", "pre-commit", "pytest (>=7.0)", "pytest-console-scripts", "pytest-jupyter[server] (>=0.4)", "pytest-timeout", "requests"] + +[[package]] +name = "jupyter-server-terminals" +version = "0.4.4" +description = "A Jupyter Server Extension Providing Terminals." +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "jupyter_server_terminals-0.4.4-py3-none-any.whl", hash = "sha256:75779164661cec02a8758a5311e18bb8eb70c4e86c6b699403100f1585a12a36"}, + {file = "jupyter_server_terminals-0.4.4.tar.gz", hash = "sha256:57ab779797c25a7ba68e97bcfb5d7740f2b5e8a83b5e8102b10438041a7eac5d"}, +] + +[package.dependencies] +pywinpty = {version = ">=2.0.3", markers = "os_name == \"nt\""} +terminado = ">=0.8.3" + +[package.extras] +docs = ["jinja2", "jupyter-server", "mistune (<3.0)", "myst-parser", "nbformat", "packaging", "pydata-sphinx-theme", "sphinxcontrib-github-alt", "sphinxcontrib-openapi", "sphinxcontrib-spelling", "sphinxemoji", "tornado"] +test = ["coverage", "jupyter-server (>=2.0.0)", "pytest (>=7.0)", "pytest-cov", "pytest-jupyter[server] (>=0.5.3)", "pytest-timeout"] + +[[package]] +name = "jupyterlab-pygments" +version = "0.2.2" +description = "Pygments theme using JupyterLab CSS variables" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "jupyterlab_pygments-0.2.2-py2.py3-none-any.whl", hash = "sha256:2405800db07c9f770863bcf8049a529c3dd4d3e28536638bd7c1c01d2748309f"}, + {file = "jupyterlab_pygments-0.2.2.tar.gz", hash = "sha256:7405d7fde60819d905a9fa8ce89e4cd830e318cdad22a0030f7a901da705585d"}, +] + +[[package]] +name = "jupyterlab-widgets" +version = "3.0.5" +description = "Jupyter interactive widgets for JupyterLab" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "jupyterlab_widgets-3.0.5-py3-none-any.whl", hash = "sha256:a04a42e50231b355b7087e16a818f541e53589f7647144ea0344c4bf16f300e5"}, + {file = "jupyterlab_widgets-3.0.5.tar.gz", hash = "sha256:eeaecdeaf6c03afc960ddae201ced88d5979b4ca9c3891bcb8f6631af705f5ef"}, +] + +[[package]] +name = "keyring" +version = "23.13.1" +description = "Store and access your passwords safely." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "keyring-23.13.1-py3-none-any.whl", hash = "sha256:771ed2a91909389ed6148631de678f82ddc73737d85a927f382a8a1b157898cd"}, + {file = "keyring-23.13.1.tar.gz", hash = "sha256:ba2e15a9b35e21908d0aaf4e0a47acc52d6ae33444df0da2b49d41a46ef6d678"}, +] + +[package.dependencies] +importlib-metadata = {version = ">=4.11.4", markers = "python_version < \"3.12\""} +"jaraco.classes" = "*" +jeepney = {version = ">=0.4.2", markers = "sys_platform == \"linux\""} +pywin32-ctypes = {version = ">=0.2.0", markers = "sys_platform == \"win32\""} +SecretStorage = {version = ">=3.2", markers = "sys_platform == \"linux\""} + +[package.extras] +completion = ["shtab"] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"] +testing = ["flake8 (<5)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] + +[[package]] +name = "kiwisolver" +version = "1.4.4" +description = "A fast implementation of the Cassowary constraint solver" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "kiwisolver-1.4.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2f5e60fabb7343a836360c4f0919b8cd0d6dbf08ad2ca6b9cf90bf0c76a3c4f6"}, + {file = "kiwisolver-1.4.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:10ee06759482c78bdb864f4109886dff7b8a56529bc1609d4f1112b93fe6423c"}, + {file = "kiwisolver-1.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c79ebe8f3676a4c6630fd3f777f3cfecf9289666c84e775a67d1d358578dc2e3"}, + {file = "kiwisolver-1.4.4-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:abbe9fa13da955feb8202e215c4018f4bb57469b1b78c7a4c5c7b93001699938"}, + {file = "kiwisolver-1.4.4-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7577c1987baa3adc4b3c62c33bd1118c3ef5c8ddef36f0f2c950ae0b199e100d"}, + {file = "kiwisolver-1.4.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8ad8285b01b0d4695102546b342b493b3ccc6781fc28c8c6a1bb63e95d22f09"}, + {file = "kiwisolver-1.4.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8ed58b8acf29798b036d347791141767ccf65eee7f26bde03a71c944449e53de"}, + {file = "kiwisolver-1.4.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a68b62a02953b9841730db7797422f983935aeefceb1679f0fc85cbfbd311c32"}, + {file = "kiwisolver-1.4.4-cp310-cp310-win32.whl", hash = "sha256:e92a513161077b53447160b9bd8f522edfbed4bd9759e4c18ab05d7ef7e49408"}, + {file = "kiwisolver-1.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:3fe20f63c9ecee44560d0e7f116b3a747a5d7203376abeea292ab3152334d004"}, + {file = "kiwisolver-1.4.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e0ea21f66820452a3f5d1655f8704a60d66ba1191359b96541eaf457710a5fc6"}, + {file = "kiwisolver-1.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:bc9db8a3efb3e403e4ecc6cd9489ea2bac94244f80c78e27c31dcc00d2790ac2"}, + {file = "kiwisolver-1.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d5b61785a9ce44e5a4b880272baa7cf6c8f48a5180c3e81c59553ba0cb0821ca"}, + {file = "kiwisolver-1.4.4-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c2dbb44c3f7e6c4d3487b31037b1bdbf424d97687c1747ce4ff2895795c9bf69"}, + {file = "kiwisolver-1.4.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6295ecd49304dcf3bfbfa45d9a081c96509e95f4b9d0eb7ee4ec0530c4a96514"}, + {file = "kiwisolver-1.4.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4bd472dbe5e136f96a4b18f295d159d7f26fd399136f5b17b08c4e5f498cd494"}, + {file = "kiwisolver-1.4.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bf7d9fce9bcc4752ca4a1b80aabd38f6d19009ea5cbda0e0856983cf6d0023f5"}, + {file = "kiwisolver-1.4.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78d6601aed50c74e0ef02f4204da1816147a6d3fbdc8b3872d263338a9052c51"}, + {file = "kiwisolver-1.4.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:877272cf6b4b7e94c9614f9b10140e198d2186363728ed0f701c6eee1baec1da"}, + {file = "kiwisolver-1.4.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:db608a6757adabb32f1cfe6066e39b3706d8c3aa69bbc353a5b61edad36a5cb4"}, + {file = "kiwisolver-1.4.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:5853eb494c71e267912275e5586fe281444eb5e722de4e131cddf9d442615626"}, + {file = "kiwisolver-1.4.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:f0a1dbdb5ecbef0d34eb77e56fcb3e95bbd7e50835d9782a45df81cc46949750"}, + {file = "kiwisolver-1.4.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:283dffbf061a4ec60391d51e6155e372a1f7a4f5b15d59c8505339454f8989e4"}, + {file = "kiwisolver-1.4.4-cp311-cp311-win32.whl", hash = "sha256:d06adcfa62a4431d404c31216f0f8ac97397d799cd53800e9d3efc2fbb3cf14e"}, + {file = "kiwisolver-1.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:e7da3fec7408813a7cebc9e4ec55afed2d0fd65c4754bc376bf03498d4e92686"}, + {file = "kiwisolver-1.4.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:62ac9cc684da4cf1778d07a89bf5f81b35834cb96ca523d3a7fb32509380cbf6"}, + {file = "kiwisolver-1.4.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41dae968a94b1ef1897cb322b39360a0812661dba7c682aa45098eb8e193dbdf"}, + {file = "kiwisolver-1.4.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:02f79693ec433cb4b5f51694e8477ae83b3205768a6fb48ffba60549080e295b"}, + {file = "kiwisolver-1.4.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d0611a0a2a518464c05ddd5a3a1a0e856ccc10e67079bb17f265ad19ab3c7597"}, + {file = "kiwisolver-1.4.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:db5283d90da4174865d520e7366801a93777201e91e79bacbac6e6927cbceede"}, + {file = "kiwisolver-1.4.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:1041feb4cda8708ce73bb4dcb9ce1ccf49d553bf87c3954bdfa46f0c3f77252c"}, + {file = "kiwisolver-1.4.4-cp37-cp37m-win32.whl", hash = "sha256:a553dadda40fef6bfa1456dc4be49b113aa92c2a9a9e8711e955618cd69622e3"}, + {file = "kiwisolver-1.4.4-cp37-cp37m-win_amd64.whl", hash = "sha256:03baab2d6b4a54ddbb43bba1a3a2d1627e82d205c5cf8f4c924dc49284b87166"}, + {file = "kiwisolver-1.4.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:841293b17ad704d70c578f1f0013c890e219952169ce8a24ebc063eecf775454"}, + {file = "kiwisolver-1.4.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f4f270de01dd3e129a72efad823da90cc4d6aafb64c410c9033aba70db9f1ff0"}, + {file = "kiwisolver-1.4.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f9f39e2f049db33a908319cf46624a569b36983c7c78318e9726a4cb8923b26c"}, + {file = "kiwisolver-1.4.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c97528e64cb9ebeff9701e7938653a9951922f2a38bd847787d4a8e498cc83ae"}, + {file = "kiwisolver-1.4.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d1573129aa0fd901076e2bfb4275a35f5b7aa60fbfb984499d661ec950320b0"}, + {file = "kiwisolver-1.4.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ad881edc7ccb9d65b0224f4e4d05a1e85cf62d73aab798943df6d48ab0cd79a1"}, + {file = "kiwisolver-1.4.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b428ef021242344340460fa4c9185d0b1f66fbdbfecc6c63eff4b7c29fad429d"}, + {file = "kiwisolver-1.4.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:2e407cb4bd5a13984a6c2c0fe1845e4e41e96f183e5e5cd4d77a857d9693494c"}, + {file = "kiwisolver-1.4.4-cp38-cp38-win32.whl", hash = "sha256:75facbe9606748f43428fc91a43edb46c7ff68889b91fa31f53b58894503a191"}, + {file = "kiwisolver-1.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:5bce61af018b0cb2055e0e72e7d65290d822d3feee430b7b8203d8a855e78766"}, + {file = "kiwisolver-1.4.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8c808594c88a025d4e322d5bb549282c93c8e1ba71b790f539567932722d7bd8"}, + {file = "kiwisolver-1.4.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f0a71d85ecdd570ded8ac3d1c0f480842f49a40beb423bb8014539a9f32a5897"}, + {file = "kiwisolver-1.4.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b533558eae785e33e8c148a8d9921692a9fe5aa516efbdff8606e7d87b9d5824"}, + {file = "kiwisolver-1.4.4-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:efda5fc8cc1c61e4f639b8067d118e742b812c930f708e6667a5ce0d13499e29"}, + {file = "kiwisolver-1.4.4-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7c43e1e1206cd421cd92e6b3280d4385d41d7166b3ed577ac20444b6995a445f"}, + {file = "kiwisolver-1.4.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc8d3bd6c72b2dd9decf16ce70e20abcb3274ba01b4e1c96031e0c4067d1e7cd"}, + {file = "kiwisolver-1.4.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4ea39b0ccc4f5d803e3337dd46bcce60b702be4d86fd0b3d7531ef10fd99a1ac"}, + {file = "kiwisolver-1.4.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:968f44fdbf6dd757d12920d63b566eeb4d5b395fd2d00d29d7ef00a00582aac9"}, + {file = "kiwisolver-1.4.4-cp39-cp39-win32.whl", hash = "sha256:da7e547706e69e45d95e116e6939488d62174e033b763ab1496b4c29b76fabea"}, + {file = "kiwisolver-1.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:ba59c92039ec0a66103b1d5fe588fa546373587a7d68f5c96f743c3396afc04b"}, + {file = "kiwisolver-1.4.4-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:91672bacaa030f92fc2f43b620d7b337fd9a5af28b0d6ed3f77afc43c4a64b5a"}, + {file = "kiwisolver-1.4.4-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:787518a6789009c159453da4d6b683f468ef7a65bbde796bcea803ccf191058d"}, + {file = "kiwisolver-1.4.4-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da152d8cdcab0e56e4f45eb08b9aea6455845ec83172092f09b0e077ece2cf7a"}, + {file = "kiwisolver-1.4.4-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:ecb1fa0db7bf4cff9dac752abb19505a233c7f16684c5826d1f11ebd9472b871"}, + {file = "kiwisolver-1.4.4-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:28bc5b299f48150b5f822ce68624e445040595a4ac3d59251703779836eceff9"}, + {file = "kiwisolver-1.4.4-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:81e38381b782cc7e1e46c4e14cd997ee6040768101aefc8fa3c24a4cc58e98f8"}, + {file = "kiwisolver-1.4.4-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2a66fdfb34e05b705620dd567f5a03f239a088d5a3f321e7b6ac3239d22aa286"}, + {file = "kiwisolver-1.4.4-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:872b8ca05c40d309ed13eb2e582cab0c5a05e81e987ab9c521bf05ad1d5cf5cb"}, + {file = "kiwisolver-1.4.4-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:70e7c2e7b750585569564e2e5ca9845acfaa5da56ac46df68414f29fea97be9f"}, + {file = "kiwisolver-1.4.4-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:9f85003f5dfa867e86d53fac6f7e6f30c045673fa27b603c397753bebadc3008"}, + {file = "kiwisolver-1.4.4-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e307eb9bd99801f82789b44bb45e9f541961831c7311521b13a6c85afc09767"}, + {file = "kiwisolver-1.4.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1792d939ec70abe76f5054d3f36ed5656021dcad1322d1cc996d4e54165cef9"}, + {file = "kiwisolver-1.4.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6cb459eea32a4e2cf18ba5fcece2dbdf496384413bc1bae15583f19e567f3b2"}, + {file = "kiwisolver-1.4.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:36dafec3d6d6088d34e2de6b85f9d8e2324eb734162fba59d2ba9ed7a2043d5b"}, + {file = "kiwisolver-1.4.4.tar.gz", hash = "sha256:d41997519fcba4a1e46eb4a2fe31bc12f0ff957b2b81bac28db24744f333e955"}, +] + +[[package]] +name = "lockfile" +version = "0.12.2" +description = "Platform-independent file locking module" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "lockfile-0.12.2-py2.py3-none-any.whl", hash = "sha256:6c3cb24f344923d30b2785d5ad75182c8ea7ac1b6171b08657258ec7429d50fa"}, + {file = "lockfile-0.12.2.tar.gz", hash = "sha256:6aed02de03cba24efabcd600b30540140634fc06cfa603822d508d5361e9f799"}, +] + +[[package]] +name = "markdown" +version = "3.4.1" +description = "Python implementation of Markdown." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "Markdown-3.4.1-py3-none-any.whl", hash = "sha256:08fb8465cffd03d10b9dd34a5c3fea908e20391a2a90b88d66362cb05beed186"}, + {file = "Markdown-3.4.1.tar.gz", hash = "sha256:3b809086bb6efad416156e00a0da66fe47618a5d6918dd688f53f40c8e4cfeff"}, +] + +[package.extras] +testing = ["coverage", "pyyaml"] + +[[package]] +name = "markupsafe" +version = "2.1.1" +description = "Safely add untrusted strings to HTML/XML markup." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-win32.whl", hash = "sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-win32.whl", hash = "sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-win32.whl", hash = "sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-win32.whl", hash = "sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247"}, + {file = "MarkupSafe-2.1.1.tar.gz", hash = "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b"}, +] + +[[package]] +name = "matplotlib" +version = "3.6.3" +description = "Python plotting package" +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "matplotlib-3.6.3-cp310-cp310-macosx_10_12_universal2.whl", hash = "sha256:80c166a0e28512e26755f69040e6bf2f946a02ffdb7c00bf6158cca3d2b146e6"}, + {file = "matplotlib-3.6.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:eb9421c403ffd387fbe729de6d9a03005bf42faba5e8432f4e51e703215b49fc"}, + {file = "matplotlib-3.6.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5223affa21050fb6118353c1380c15e23aedfb436bf3e162c26dc950617a7519"}, + {file = "matplotlib-3.6.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d00c248ab6b92bea3f8148714837937053a083ff03b4c5e30ed37e28fc0e7e56"}, + {file = "matplotlib-3.6.3-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca94f0362f6b6f424b555b956971dcb94b12d0368a6c3e07dc7a40d32d6d873d"}, + {file = "matplotlib-3.6.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59400cc9451094b7f08cc3f321972e6e1db4cd37a978d4e8a12824bf7fd2f03b"}, + {file = "matplotlib-3.6.3-cp310-cp310-win32.whl", hash = "sha256:57ad1aee29043163374bfa8990e1a2a10ff72c9a1bfaa92e9c46f6ea59269121"}, + {file = "matplotlib-3.6.3-cp310-cp310-win_amd64.whl", hash = "sha256:1fcc4cad498533d3c393a160975acc9b36ffa224d15a6b90ae579eacee5d8579"}, + {file = "matplotlib-3.6.3-cp311-cp311-macosx_10_12_universal2.whl", hash = "sha256:d2cfaa7fd62294d945b8843ea24228a27c8e7c5b48fa634f3c168153b825a21b"}, + {file = "matplotlib-3.6.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:c3f08df2ac4636249b8bc7a85b8b82c983bef1441595936f62c2918370ca7e1d"}, + {file = "matplotlib-3.6.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ff2aa84e74f80891e6bcf292ebb1dd57714ffbe13177642d65fee25384a30894"}, + {file = "matplotlib-3.6.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:11011c97d62c1db7bc20509572557842dbb8c2a2ddd3dd7f20501aa1cde3e54e"}, + {file = "matplotlib-3.6.3-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c235bf9be052347373f589e018988cad177abb3f997ab1a2e2210c41562cc0c"}, + {file = "matplotlib-3.6.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bebcff4c3ed02c6399d47329f3554193abd824d3d53b5ca02cf583bcd94470e2"}, + {file = "matplotlib-3.6.3-cp311-cp311-win32.whl", hash = "sha256:d5f18430f5cfa5571ab8f4c72c89af52aa0618e864c60028f11a857d62200cba"}, + {file = "matplotlib-3.6.3-cp311-cp311-win_amd64.whl", hash = "sha256:dfba7057609ca9567b9704626756f0142e97ec8c5ba2c70c6e7bd1c25ef99f06"}, + {file = "matplotlib-3.6.3-cp38-cp38-macosx_10_12_universal2.whl", hash = "sha256:9fb8fb19d03abf3c5dab89a8677e62c4023632f919a62b6dd1d6d2dbf42cd9f5"}, + {file = "matplotlib-3.6.3-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:bbf269e1d24bc25247095d71c7a969813f7080e2a7c6fa28931a603f747ab012"}, + {file = "matplotlib-3.6.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:994637e2995b0342699b396a320698b07cd148bbcf2dd2fa2daba73f34dd19f2"}, + {file = "matplotlib-3.6.3-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:77b384cee7ab8cf75ffccbfea351a09b97564fc62d149827a5e864bec81526e5"}, + {file = "matplotlib-3.6.3-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:73b93af33634ed919e72811c9703e1105185cd3fb46d76f30b7f4cfbbd063f89"}, + {file = "matplotlib-3.6.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:debeab8e2ab07e5e3dac33e12456da79c7e104270d2b2d1df92b9e40347cca75"}, + {file = "matplotlib-3.6.3-cp38-cp38-win32.whl", hash = "sha256:acc3b1a4bddbf56fe461e36fb9ef94c2cb607fc90d24ccc650040bfcc7610de4"}, + {file = "matplotlib-3.6.3-cp38-cp38-win_amd64.whl", hash = "sha256:1183877d008c752d7d535396096c910f4663e4b74a18313adee1213328388e1e"}, + {file = "matplotlib-3.6.3-cp39-cp39-macosx_10_12_universal2.whl", hash = "sha256:6adc441b5b2098a4b904bbf9d9e92fb816fef50c55aa2ea6a823fc89b94bb838"}, + {file = "matplotlib-3.6.3-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:6d81b11ede69e3a751424b98dc869c96c10256b2206bfdf41f9c720eee86844c"}, + {file = "matplotlib-3.6.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:29f17b7f2e068dc346687cbdf80b430580bab42346625821c2d3abf3a1ec5417"}, + {file = "matplotlib-3.6.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f56a7252eee8f3438447f75f5e1148a1896a2756a92285fe5d73bed6deebff4"}, + {file = "matplotlib-3.6.3-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bbddfeb1495484351fb5b30cf5bdf06b3de0bc4626a707d29e43dfd61af2a780"}, + {file = "matplotlib-3.6.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:809119d1cba3ece3c9742eb01827fe7a0e781ea3c5d89534655a75e07979344f"}, + {file = "matplotlib-3.6.3-cp39-cp39-win32.whl", hash = "sha256:e0a64d7cc336b52e90f59e6d638ae847b966f68582a7af041e063d568e814740"}, + {file = "matplotlib-3.6.3-cp39-cp39-win_amd64.whl", hash = "sha256:79e501eb847f4a489eb7065bb8d3187117f65a4c02d12ea3a19d6c5bef173bcc"}, + {file = "matplotlib-3.6.3-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2787a16df07370dcba385fe20cdd0cc3cfaabd3c873ddabca78c10514c799721"}, + {file = "matplotlib-3.6.3-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:68d94a436f62b8a861bf3ace82067a71bafb724b4e4f9133521e4d8012420dd7"}, + {file = "matplotlib-3.6.3-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81b409b2790cf8d7c1ef35920f01676d2ae7afa8241844e7aa5484fdf493a9a0"}, + {file = "matplotlib-3.6.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:faff486b36530a836a6b4395850322e74211cd81fc17f28b4904e1bd53668e3e"}, + {file = "matplotlib-3.6.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:38d38cb1ea1d80ee0f6351b65c6f76cad6060bbbead015720ba001348ae90f0c"}, + {file = "matplotlib-3.6.3-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:12f999661589981e74d793ee2f41b924b3b87d65fd929f6153bf0f30675c59b1"}, + {file = "matplotlib-3.6.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01b7f521a9a73c383825813af255f8c4485d1706e4f3e2ed5ae771e4403a40ab"}, + {file = "matplotlib-3.6.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:9ceebaf73f1a3444fa11014f38b9da37ff7ea328d6efa1652241fe3777bfdab9"}, + {file = "matplotlib-3.6.3.tar.gz", hash = "sha256:1f4d69707b1677560cd952544ee4962f68ff07952fb9069ff8c12b56353cb8c9"}, +] + +[package.dependencies] +contourpy = ">=1.0.1" +cycler = ">=0.10" +fonttools = ">=4.22.0" +kiwisolver = ">=1.0.1" +numpy = ">=1.19" +packaging = ">=20.0" +pillow = ">=6.2.0" +pyparsing = ">=2.2.1" +python-dateutil = ">=2.7" + +[[package]] +name = "matplotlib-inline" +version = "0.1.6" +description = "Inline Matplotlib backend for Jupyter" +category = "main" +optional = false +python-versions = ">=3.5" +files = [ + {file = "matplotlib-inline-0.1.6.tar.gz", hash = "sha256:f887e5f10ba98e8d2b150ddcf4702c1e5f8b3a20005eb0f74bfdbd360ee6f304"}, + {file = "matplotlib_inline-0.1.6-py3-none-any.whl", hash = "sha256:f1f41aab5328aa5aaea9b16d083b128102f8712542f819fe7e6a420ff581b311"}, +] + +[package.dependencies] +traitlets = "*" + +[[package]] +name = "mistune" +version = "2.0.4" +description = "A sane Markdown parser with useful plugins and renderers" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "mistune-2.0.4-py2.py3-none-any.whl", hash = "sha256:182cc5ee6f8ed1b807de6b7bb50155df7b66495412836b9a74c8fbdfc75fe36d"}, + {file = "mistune-2.0.4.tar.gz", hash = "sha256:9ee0a66053e2267aba772c71e06891fa8f1af6d4b01d5e84e267b4570d4d9808"}, +] + +[[package]] +name = "moderngl" +version = "5.7.4" +description = "ModernGL: High performance rendering for Python 3" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "moderngl-5.7.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d1719892e83bda0b433b254f1be22a41745ee6b5a4fd3a6ee5aa799756383d4f"}, + {file = "moderngl-5.7.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c6fdea10f5176b8018d2e5a05ed9fa5010b8ab24fcbabe64c05a943d50b9ba7d"}, + {file = "moderngl-5.7.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:396b2bc0a09d409b40e7c559bb1092ee327f5394b125e2ebfa9fc2951e27550d"}, + {file = "moderngl-5.7.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8eea6a083e8737bbe666e83c460a2628556f7df489987405fe33b27db3df386c"}, + {file = "moderngl-5.7.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca77be80c96c0585d7dd0f0b65815883fb5416f22de60875bec0f87f8c1c2b6b"}, + {file = "moderngl-5.7.4-cp310-cp310-win32.whl", hash = "sha256:48c3677f0303a2c5887dccd4ff3c06cc60bc54f05646bb4575f87dd69f65f83b"}, + {file = "moderngl-5.7.4-cp310-cp310-win_amd64.whl", hash = "sha256:7d28d49892923c2cc8eb145266af95ed99a0c4f3fc686228af941d1e0cdbd9bb"}, + {file = "moderngl-5.7.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:dd2446134c7bd71c8d47606545ca7820cb411723edf77d2b09301651a2356e91"}, + {file = "moderngl-5.7.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d549c518624975652ad511659d2b27827d2aa9ccbf09b17b4b53afe4226b68c7"}, + {file = "moderngl-5.7.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4441cc13384c41b7434eb1eab42f1c305f6c9b3dd8665e98ad8dc1870ff83d38"}, + {file = "moderngl-5.7.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc921e7b716a0260eeb352d3c6fd1c0fa5edb434de89e0325fc58611d3929f9e"}, + {file = "moderngl-5.7.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bdab10cbe2b87394440a3678c1445fe7fe687e82fd6b3ae7ddd76ec68766115a"}, + {file = "moderngl-5.7.4-cp311-cp311-win32.whl", hash = "sha256:631876e886aebf293a9afdf54160d46f1dc2646631750837ef04518044c4de5f"}, + {file = "moderngl-5.7.4-cp311-cp311-win_amd64.whl", hash = "sha256:38c5409df54f6601b4df94f61609ef7519bb570061333ba2f7d58bddef0963df"}, + {file = "moderngl-5.7.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:875fe5701061a7bbe833cbd42cd779c68748ed277b2ab1ec06be61972e3e846f"}, + {file = "moderngl-5.7.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eb49672482406f539968b10f30d982578242a850dd7f071dc57b34fa88aebe3d"}, + {file = "moderngl-5.7.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f502d3ac58a776a57f1f524efbd05b6b95a601e087082dc8cd5cd71cb315c577"}, + {file = "moderngl-5.7.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:918f11262c4724e2515c4afc76ad57c6148d486fd6e7c3b1c04470f43f57f97b"}, + {file = "moderngl-5.7.4-cp37-cp37m-win32.whl", hash = "sha256:dce7cb87d7faced6f78e06c7e2fa607a79a50cb8ff0aed3dc9bca77b814ee042"}, + {file = "moderngl-5.7.4-cp37-cp37m-win_amd64.whl", hash = "sha256:dd010370e30bc52e65baee5061eb2ec6965adc2b08e81c232ccf7ec76553af5e"}, + {file = "moderngl-5.7.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:49546570a7eb4594cc1a4109fdef47a035ffb0b11cc3d38aafe92ab81dc6d50f"}, + {file = "moderngl-5.7.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:824f6dc34316530a617fea9e6a46d008cfe82a01e9d34ae308ad51ef127720a7"}, + {file = "moderngl-5.7.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6cb8c76c77c47cf05c29bd1940d95a641ad1b6052b5b0d29ffca816295ca364e"}, + {file = "moderngl-5.7.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b923f6644989cde6675394f63027914546fc33a380148cec449176980c25e47"}, + {file = "moderngl-5.7.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fe9a40751133744018dcb19289a1f798908b1eff1a8455803dfe1bc4028f0f34"}, + {file = "moderngl-5.7.4-cp38-cp38-win32.whl", hash = "sha256:fcc0e5b557a67137a5c6944faf4dde096cf5afab296d11e86bbf20ead043eddf"}, + {file = "moderngl-5.7.4-cp38-cp38-win_amd64.whl", hash = "sha256:8ffbad06fb39116406a8988d19fd073bbbf973513a376b3c6b181ae67408237b"}, + {file = "moderngl-5.7.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:82cd399b152f3a775c1f93ef3192980a9d3d2d1c51a9df07a329ce4e4fc82430"}, + {file = "moderngl-5.7.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:eb6ad78ecfc06c0aab0be9706b71be5ee0ad443efbc4f6578a315da88dc3d4ef"}, + {file = "moderngl-5.7.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1d6c79fad12df622fa0dc8b832a2c624368cd949a0326f65ebc234891726068"}, + {file = "moderngl-5.7.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9630a42cefafaeb19a816e23de2869d855af39b880c38d9efc16b50e354297c4"}, + {file = "moderngl-5.7.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:395f96f81ea9b3c8fcd9cf1e5aedd9fe81ef0a2475922fe8f1657fbfdbd03397"}, + {file = "moderngl-5.7.4-cp39-cp39-win32.whl", hash = "sha256:512d9287a9e80bdc706c6f54b190820d8f3887619a574d5f996eb27ff2385777"}, + {file = "moderngl-5.7.4-cp39-cp39-win_amd64.whl", hash = "sha256:664a0089abc845bba2198fcadbe6d78adf65e4e5f8e117147f0d36353e9a2090"}, + {file = "moderngl-5.7.4-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:2f8ec6ddb6141751f35734e8ce2222868da3fca57fbcb83ce4f28c93a5fcb91c"}, + {file = "moderngl-5.7.4-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4660a33a093d44237d40adf884a4b678dc7c1237a6d634a2ffa4652cd476aa19"}, + {file = "moderngl-5.7.4-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70477f355e45a8c2cce4f97818191ab28313e4203b15554214a417853ee8cfcb"}, + {file = "moderngl-5.7.4-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:35f5e916d3d33753ec7a0611b5cf5addbe88a8a1aa3d292f2693808381bb19c5"}, + {file = "moderngl-5.7.4-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:a2e50913ab8e53b3c8da792f4b75ff7afcd117e596346110ec085de535d03ef1"}, + {file = "moderngl-5.7.4-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:9dc27da2e9225fc9df21fa0522cd77e781ee0a2d7b75a03449c963516fb9b96c"}, + {file = "moderngl-5.7.4-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bfd84ac24d141b490293487e63eacebc0f198d12049adfe44d08168fc89f979a"}, + {file = "moderngl-5.7.4-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3cde28a2091222f51a743561cebc8d80314042a7db9a8e723e8fa5f1ad6618d"}, + {file = "moderngl-5.7.4-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f7579e66c345342f9b3fdf33e23107b874499979577b14014249856e73071c06"}, + {file = "moderngl-5.7.4-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:3a6d180fab423cbbaac1ee37e4d76821bf3a13e79776597c85199876f3053635"}, + {file = "moderngl-5.7.4-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:9c3e825088a0acfb0080f3ccc0332584f99868ed39bf986e44f03581f6d33128"}, + {file = "moderngl-5.7.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:44a69c62e468fd71c84384041242194f81a88fdfe08799a5f01393729c9928bd"}, + {file = "moderngl-5.7.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a7963e49cadb621b7ffbd5effe43ce6472afb535dd5c074924b95fdce54d289e"}, + {file = "moderngl-5.7.4-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8c226cb05022372b4e6b841b47d7c67d9da026d93dd38801351ce679e70beacf"}, + {file = "moderngl-5.7.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a20a575d9a5cab62554fbcb7b53e5459a13f380ee2e5c4888e9b537ef575f1ca"}, + {file = "moderngl-5.7.4.tar.gz", hash = "sha256:20f821bf66b2811bc8648d7cf7f64402afff7619fea271f42a6ee85fe03e4041"}, +] + +[package.dependencies] +glcontext = ">=2.3.6,<3" + +[[package]] +name = "more-itertools" +version = "9.0.0" +description = "More routines for operating on iterables, beyond itertools" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "more-itertools-9.0.0.tar.gz", hash = "sha256:5a6257e40878ef0520b1803990e3e22303a41b5714006c32a3fd8304b26ea1ab"}, + {file = "more_itertools-9.0.0-py3-none-any.whl", hash = "sha256:250e83d7e81d0c87ca6bd942e6aeab8cc9daa6096d12c5308f3f92fa5e5c1f41"}, +] + +[[package]] +name = "msgpack" +version = "1.0.4" +description = "MessagePack serializer" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "msgpack-1.0.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:4ab251d229d10498e9a2f3b1e68ef64cb393394ec477e3370c457f9430ce9250"}, + {file = "msgpack-1.0.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:112b0f93202d7c0fef0b7810d465fde23c746a2d482e1e2de2aafd2ce1492c88"}, + {file = "msgpack-1.0.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:002b5c72b6cd9b4bafd790f364b8480e859b4712e91f43014fe01e4f957b8467"}, + {file = "msgpack-1.0.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:35bc0faa494b0f1d851fd29129b2575b2e26d41d177caacd4206d81502d4c6a6"}, + {file = "msgpack-1.0.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4733359808c56d5d7756628736061c432ded018e7a1dff2d35a02439043321aa"}, + {file = "msgpack-1.0.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb514ad14edf07a1dbe63761fd30f89ae79b42625731e1ccf5e1f1092950eaa6"}, + {file = "msgpack-1.0.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c23080fdeec4716aede32b4e0ef7e213c7b1093eede9ee010949f2a418ced6ba"}, + {file = "msgpack-1.0.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:49565b0e3d7896d9ea71d9095df15b7f75a035c49be733051c34762ca95bbf7e"}, + {file = "msgpack-1.0.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:aca0f1644d6b5a73eb3e74d4d64d5d8c6c3d577e753a04c9e9c87d07692c58db"}, + {file = "msgpack-1.0.4-cp310-cp310-win32.whl", hash = "sha256:0dfe3947db5fb9ce52aaea6ca28112a170db9eae75adf9339a1aec434dc954ef"}, + {file = "msgpack-1.0.4-cp310-cp310-win_amd64.whl", hash = "sha256:4dea20515f660aa6b7e964433b1808d098dcfcabbebeaaad240d11f909298075"}, + {file = "msgpack-1.0.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:e83f80a7fec1a62cf4e6c9a660e39c7f878f603737a0cdac8c13131d11d97f52"}, + {file = "msgpack-1.0.4-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c11a48cf5e59026ad7cb0dc29e29a01b5a66a3e333dc11c04f7e991fc5510a9"}, + {file = "msgpack-1.0.4-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1276e8f34e139aeff1c77a3cefb295598b504ac5314d32c8c3d54d24fadb94c9"}, + {file = "msgpack-1.0.4-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c9566f2c39ccced0a38d37c26cc3570983b97833c365a6044edef3574a00c08"}, + {file = "msgpack-1.0.4-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:fcb8a47f43acc113e24e910399376f7277cf8508b27e5b88499f053de6b115a8"}, + {file = "msgpack-1.0.4-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:76ee788122de3a68a02ed6f3a16bbcd97bc7c2e39bd4d94be2f1821e7c4a64e6"}, + {file = "msgpack-1.0.4-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:0a68d3ac0104e2d3510de90a1091720157c319ceeb90d74f7b5295a6bee51bae"}, + {file = "msgpack-1.0.4-cp36-cp36m-win32.whl", hash = "sha256:85f279d88d8e833ec015650fd15ae5eddce0791e1e8a59165318f371158efec6"}, + {file = "msgpack-1.0.4-cp36-cp36m-win_amd64.whl", hash = "sha256:c1683841cd4fa45ac427c18854c3ec3cd9b681694caf5bff04edb9387602d661"}, + {file = "msgpack-1.0.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a75dfb03f8b06f4ab093dafe3ddcc2d633259e6c3f74bb1b01996f5d8aa5868c"}, + {file = "msgpack-1.0.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9667bdfdf523c40d2511f0e98a6c9d3603be6b371ae9a238b7ef2dc4e7a427b0"}, + {file = "msgpack-1.0.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11184bc7e56fd74c00ead4f9cc9a3091d62ecb96e97653add7a879a14b003227"}, + {file = "msgpack-1.0.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ac5bd7901487c4a1dd51a8c58f2632b15d838d07ceedaa5e4c080f7190925bff"}, + {file = "msgpack-1.0.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1e91d641d2bfe91ba4c52039adc5bccf27c335356055825c7f88742c8bb900dd"}, + {file = "msgpack-1.0.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2a2df1b55a78eb5f5b7d2a4bb221cd8363913830145fad05374a80bf0877cb1e"}, + {file = "msgpack-1.0.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:545e3cf0cf74f3e48b470f68ed19551ae6f9722814ea969305794645da091236"}, + {file = "msgpack-1.0.4-cp37-cp37m-win32.whl", hash = "sha256:2cc5ca2712ac0003bcb625c96368fd08a0f86bbc1a5578802512d87bc592fe44"}, + {file = "msgpack-1.0.4-cp37-cp37m-win_amd64.whl", hash = "sha256:eba96145051ccec0ec86611fe9cf693ce55f2a3ce89c06ed307de0e085730ec1"}, + {file = "msgpack-1.0.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:7760f85956c415578c17edb39eed99f9181a48375b0d4a94076d84148cf67b2d"}, + {file = "msgpack-1.0.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:449e57cc1ff18d3b444eb554e44613cffcccb32805d16726a5494038c3b93dab"}, + {file = "msgpack-1.0.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d603de2b8d2ea3f3bcb2efe286849aa7a81531abc52d8454da12f46235092bcb"}, + {file = "msgpack-1.0.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48f5d88c99f64c456413d74a975bd605a9b0526293218a3b77220a2c15458ba9"}, + {file = "msgpack-1.0.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6916c78f33602ecf0509cc40379271ba0f9ab572b066bd4bdafd7434dee4bc6e"}, + {file = "msgpack-1.0.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:81fc7ba725464651190b196f3cd848e8553d4d510114a954681fd0b9c479d7e1"}, + {file = "msgpack-1.0.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:d5b5b962221fa2c5d3a7f8133f9abffc114fe218eb4365e40f17732ade576c8e"}, + {file = "msgpack-1.0.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:77ccd2af37f3db0ea59fb280fa2165bf1b096510ba9fe0cc2bf8fa92a22fdb43"}, + {file = "msgpack-1.0.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b17be2478b622939e39b816e0aa8242611cc8d3583d1cd8ec31b249f04623243"}, + {file = "msgpack-1.0.4-cp38-cp38-win32.whl", hash = "sha256:2bb8cdf50dd623392fa75525cce44a65a12a00c98e1e37bf0fb08ddce2ff60d2"}, + {file = "msgpack-1.0.4-cp38-cp38-win_amd64.whl", hash = "sha256:26b8feaca40a90cbe031b03d82b2898bf560027160d3eae1423f4a67654ec5d6"}, + {file = "msgpack-1.0.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:462497af5fd4e0edbb1559c352ad84f6c577ffbbb708566a0abaaa84acd9f3ae"}, + {file = "msgpack-1.0.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2999623886c5c02deefe156e8f869c3b0aaeba14bfc50aa2486a0415178fce55"}, + {file = "msgpack-1.0.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f0029245c51fd9473dc1aede1160b0a29f4a912e6b1dd353fa6d317085b219da"}, + {file = "msgpack-1.0.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed6f7b854a823ea44cf94919ba3f727e230da29feb4a99711433f25800cf747f"}, + {file = "msgpack-1.0.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0df96d6eaf45ceca04b3f3b4b111b86b33785683d682c655063ef8057d61fd92"}, + {file = "msgpack-1.0.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6a4192b1ab40f8dca3f2877b70e63799d95c62c068c84dc028b40a6cb03ccd0f"}, + {file = "msgpack-1.0.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0e3590f9fb9f7fbc36df366267870e77269c03172d086fa76bb4eba8b2b46624"}, + {file = "msgpack-1.0.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:1576bd97527a93c44fa856770197dec00d223b0b9f36ef03f65bac60197cedf8"}, + {file = "msgpack-1.0.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:63e29d6e8c9ca22b21846234913c3466b7e4ee6e422f205a2988083de3b08cae"}, + {file = "msgpack-1.0.4-cp39-cp39-win32.whl", hash = "sha256:fb62ea4b62bfcb0b380d5680f9a4b3f9a2d166d9394e9bbd9666c0ee09a3645c"}, + {file = "msgpack-1.0.4-cp39-cp39-win_amd64.whl", hash = "sha256:4d5834a2a48965a349da1c5a79760d94a1a0172fbb5ab6b5b33cbf8447e109ce"}, + {file = "msgpack-1.0.4.tar.gz", hash = "sha256:f5d869c18f030202eb412f08b28d2afeea553d6613aee89e200d7aca7ef01f5f"}, +] + +[[package]] +name = "nbclassic" +version = "0.4.8" +description = "A web-based notebook environment for interactive computing" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "nbclassic-0.4.8-py3-none-any.whl", hash = "sha256:cbf05df5842b420d5cece0143462380ea9d308ff57c2dc0eb4d6e035b18fbfb3"}, + {file = "nbclassic-0.4.8.tar.gz", hash = "sha256:c74d8a500f8e058d46b576a41e5bc640711e1032cf7541dde5f73ea49497e283"}, +] + +[package.dependencies] +argon2-cffi = "*" +ipykernel = "*" +ipython-genutils = "*" +jinja2 = "*" +jupyter-client = ">=6.1.1" +jupyter-core = ">=4.6.1" +jupyter-server = ">=1.8" +nbconvert = ">=5" +nbformat = "*" +nest-asyncio = ">=1.5" +notebook-shim = ">=0.1.0" +prometheus-client = "*" +pyzmq = ">=17" +Send2Trash = ">=1.8.0" +terminado = ">=0.8.3" +tornado = ">=6.1" +traitlets = ">=4.2.1" + +[package.extras] +docs = ["myst-parser", "nbsphinx", "sphinx", "sphinx-rtd-theme", "sphinxcontrib-github-alt"] +json-logging = ["json-logging"] +test = ["coverage", "nbval", "pytest", "pytest-cov", "pytest-playwright", "pytest-tornasync", "requests", "requests-unixsocket", "testpath"] + +[[package]] +name = "nbclient" +version = "0.7.2" +description = "A client library for executing notebooks. Formerly nbconvert's ExecutePreprocessor." +category = "main" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "nbclient-0.7.2-py3-none-any.whl", hash = "sha256:d97ac6257de2794f5397609df754fcbca1a603e94e924eb9b99787c031ae2e7c"}, + {file = "nbclient-0.7.2.tar.gz", hash = "sha256:884a3f4a8c4fc24bb9302f263e0af47d97f0d01fe11ba714171b320c8ac09547"}, +] + +[package.dependencies] +jupyter-client = ">=6.1.12" +jupyter-core = ">=4.12,<5.0.0 || >=5.1.0" +nbformat = ">=5.1" +traitlets = ">=5.3" + +[package.extras] +dev = ["pre-commit"] +docs = ["autodoc-traits", "mock", "moto", "myst-parser", "nbclient[test]", "sphinx (>=1.7)", "sphinx-book-theme"] +test = ["ipykernel", "ipython", "ipywidgets", "nbconvert (>=7.0.0)", "pytest (>=7.0)", "pytest-asyncio", "pytest-cov (>=4.0)", "testpath", "xmltodict"] + +[[package]] +name = "nbconvert" +version = "7.2.8" +description = "Converting Jupyter Notebooks" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "nbconvert-7.2.8-py3-none-any.whl", hash = "sha256:ac57f2812175441a883f50c8ff113133ca65fe7ae5a9f1e3da3bfd1a70dce2ee"}, + {file = "nbconvert-7.2.8.tar.gz", hash = "sha256:ccedacde57a972836bfb46466485be29ed1364ed7c2f379f62bad47d340ece99"}, +] + +[package.dependencies] +beautifulsoup4 = "*" +bleach = "*" +defusedxml = "*" +jinja2 = ">=3.0" +jupyter-core = ">=4.7" +jupyterlab-pygments = "*" +markupsafe = ">=2.0" +mistune = ">=2.0.3,<3" +nbclient = ">=0.5.0" +nbformat = ">=5.1" +packaging = "*" +pandocfilters = ">=1.4.1" +pygments = ">=2.4.1" +tinycss2 = "*" +traitlets = ">=5.0" + +[package.extras] +all = ["nbconvert[docs,qtpdf,serve,test,webpdf]"] +docs = ["ipykernel", "ipython", "myst-parser", "nbsphinx (>=0.2.12)", "pydata-sphinx-theme", "sphinx (==5.0.2)", "sphinxcontrib-spelling"] +qtpdf = ["nbconvert[qtpng]"] +qtpng = ["pyqtwebengine (>=5.15)"] +serve = ["tornado (>=6.1)"] +test = ["ipykernel", "ipywidgets (>=7)", "pre-commit", "pytest", "pytest-dependency"] +webpdf = ["pyppeteer (>=1,<1.1)"] + +[[package]] +name = "nbformat" +version = "5.7.3" +description = "The Jupyter Notebook format" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "nbformat-5.7.3-py3-none-any.whl", hash = "sha256:22a98a6516ca216002b0a34591af5bcb8072ca6c63910baffc901cfa07fefbf0"}, + {file = "nbformat-5.7.3.tar.gz", hash = "sha256:4b021fca24d3a747bf4e626694033d792d594705829e5e35b14ee3369f9f6477"}, +] + +[package.dependencies] +fastjsonschema = "*" +jsonschema = ">=2.6" +jupyter-core = "*" +traitlets = ">=5.1" + +[package.extras] +docs = ["myst-parser", "pydata-sphinx-theme", "sphinx", "sphinxcontrib-github-alt", "sphinxcontrib-spelling"] +test = ["pep440", "pre-commit", "pytest", "testpath"] + +[[package]] +name = "nest-asyncio" +version = "1.5.6" +description = "Patch asyncio to allow nested event loops" +category = "main" +optional = false +python-versions = ">=3.5" +files = [ + {file = "nest_asyncio-1.5.6-py3-none-any.whl", hash = "sha256:b9a953fb40dceaa587d109609098db21900182b16440652454a146cffb06e8b8"}, + {file = "nest_asyncio-1.5.6.tar.gz", hash = "sha256:d267cc1ff794403f7df692964d1d2a3fa9418ffea2a3f6859a439ff482fef290"}, +] + +[[package]] +name = "notebook" +version = "6.5.2" +description = "A web-based notebook environment for interactive computing" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "notebook-6.5.2-py3-none-any.whl", hash = "sha256:e04f9018ceb86e4fa841e92ea8fb214f8d23c1cedfde530cc96f92446924f0e4"}, + {file = "notebook-6.5.2.tar.gz", hash = "sha256:c1897e5317e225fc78b45549a6ab4b668e4c996fd03a04e938fe5e7af2bfffd0"}, +] + +[package.dependencies] +argon2-cffi = "*" +ipykernel = "*" +ipython-genutils = "*" +jinja2 = "*" +jupyter-client = ">=5.3.4" +jupyter-core = ">=4.6.1" +nbclassic = ">=0.4.7" +nbconvert = ">=5" +nbformat = "*" +nest-asyncio = ">=1.5" +prometheus-client = "*" +pyzmq = ">=17" +Send2Trash = ">=1.8.0" +terminado = ">=0.8.3" +tornado = ">=6.1" +traitlets = ">=4.2.1" + +[package.extras] +docs = ["myst-parser", "nbsphinx", "sphinx", "sphinx-rtd-theme", "sphinxcontrib-github-alt"] +json-logging = ["json-logging"] +test = ["coverage", "nbval", "pytest", "pytest-cov", "requests", "requests-unixsocket", "selenium (==4.1.5)", "testpath"] + +[[package]] +name = "notebook-shim" +version = "0.2.2" +description = "A shim layer for notebook traits and config" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "notebook_shim-0.2.2-py3-none-any.whl", hash = "sha256:9c6c30f74c4fbea6fce55c1be58e7fd0409b1c681b075dcedceb005db5026949"}, + {file = "notebook_shim-0.2.2.tar.gz", hash = "sha256:090e0baf9a5582ff59b607af523ca2db68ff216da0c69956b62cab2ef4fc9c3f"}, +] + +[package.dependencies] +jupyter-server = ">=1.8,<3" + +[package.extras] +test = ["pytest", "pytest-console-scripts", "pytest-tornasync"] + +[[package]] +name = "numexpr" +version = "2.8.4" +description = "Fast numerical expression evaluator for NumPy" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "numexpr-2.8.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a75967d46b6bd56455dd32da6285e5ffabe155d0ee61eef685bbfb8dafb2e484"}, + {file = "numexpr-2.8.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:db93cf1842f068247de631bfc8af20118bf1f9447cd929b531595a5e0efc9346"}, + {file = "numexpr-2.8.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bca95f4473b444428061d4cda8e59ac564dc7dc6a1dea3015af9805c6bc2946"}, + {file = "numexpr-2.8.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e34931089a6bafc77aaae21f37ad6594b98aa1085bb8b45d5b3cd038c3c17d9"}, + {file = "numexpr-2.8.4-cp310-cp310-win32.whl", hash = "sha256:f3a920bfac2645017110b87ddbe364c9c7a742870a4d2f6120b8786c25dc6db3"}, + {file = "numexpr-2.8.4-cp310-cp310-win_amd64.whl", hash = "sha256:6931b1e9d4f629f43c14b21d44f3f77997298bea43790cfcdb4dd98804f90783"}, + {file = "numexpr-2.8.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9400781553541f414f82eac056f2b4c965373650df9694286b9bd7e8d413f8d8"}, + {file = "numexpr-2.8.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6ee9db7598dd4001138b482342b96d78110dd77cefc051ec75af3295604dde6a"}, + {file = "numexpr-2.8.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ff5835e8af9a212e8480003d731aad1727aaea909926fd009e8ae6a1cba7f141"}, + {file = "numexpr-2.8.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:655d84eb09adfee3c09ecf4a89a512225da153fdb7de13c447404b7d0523a9a7"}, + {file = "numexpr-2.8.4-cp311-cp311-win32.whl", hash = "sha256:5538b30199bfc68886d2be18fcef3abd11d9271767a7a69ff3688defe782800a"}, + {file = "numexpr-2.8.4-cp311-cp311-win_amd64.whl", hash = "sha256:3f039321d1c17962c33079987b675fb251b273dbec0f51aac0934e932446ccc3"}, + {file = "numexpr-2.8.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c867cc36cf815a3ec9122029874e00d8fbcef65035c4a5901e9b120dd5d626a2"}, + {file = "numexpr-2.8.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:059546e8f6283ccdb47c683101a890844f667fa6d56258d48ae2ecf1b3875957"}, + {file = "numexpr-2.8.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:845a6aa0ed3e2a53239b89c1ebfa8cf052d3cc6e053c72805e8153300078c0b1"}, + {file = "numexpr-2.8.4-cp37-cp37m-win32.whl", hash = "sha256:a38664e699526cb1687aefd9069e2b5b9387da7feac4545de446141f1ef86f46"}, + {file = "numexpr-2.8.4-cp37-cp37m-win_amd64.whl", hash = "sha256:eaec59e9bf70ff05615c34a8b8d6c7bd042bd9f55465d7b495ea5436f45319d0"}, + {file = "numexpr-2.8.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b318541bf3d8326682ebada087ba0050549a16d8b3fa260dd2585d73a83d20a7"}, + {file = "numexpr-2.8.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b076db98ca65eeaf9bd224576e3ac84c05e451c0bd85b13664b7e5f7b62e2c70"}, + {file = "numexpr-2.8.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:90f12cc851240f7911a47c91aaf223dba753e98e46dff3017282e633602e76a7"}, + {file = "numexpr-2.8.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c368aa35ae9b18840e78b05f929d3a7b3abccdba9630a878c7db74ca2368339"}, + {file = "numexpr-2.8.4-cp38-cp38-win32.whl", hash = "sha256:b96334fc1748e9ec4f93d5fadb1044089d73fb08208fdb8382ed77c893f0be01"}, + {file = "numexpr-2.8.4-cp38-cp38-win_amd64.whl", hash = "sha256:a6d2d7740ae83ba5f3531e83afc4b626daa71df1ef903970947903345c37bd03"}, + {file = "numexpr-2.8.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:77898fdf3da6bb96aa8a4759a8231d763a75d848b2f2e5c5279dad0b243c8dfe"}, + {file = "numexpr-2.8.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:df35324666b693f13a016bc7957de7cc4d8801b746b81060b671bf78a52b9037"}, + {file = "numexpr-2.8.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:17ac9cfe6d0078c5fc06ba1c1bbd20b8783f28c6f475bbabd3cad53683075cab"}, + {file = "numexpr-2.8.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df3a1f6b24214a1ab826e9c1c99edf1686c8e307547a9aef33910d586f626d01"}, + {file = "numexpr-2.8.4-cp39-cp39-win32.whl", hash = "sha256:7d71add384adc9119568d7e9ffa8a35b195decae81e0abf54a2b7779852f0637"}, + {file = "numexpr-2.8.4-cp39-cp39-win_amd64.whl", hash = "sha256:9f096d707290a6a00b6ffdaf581ee37331109fb7b6c8744e9ded7c779a48e517"}, + {file = "numexpr-2.8.4.tar.gz", hash = "sha256:d5432537418d18691b9115d615d6daa17ee8275baef3edf1afbbf8bc69806147"}, +] + +[package.dependencies] +numpy = ">=1.13.3" + +[[package]] +name = "numpy" +version = "1.24.1" +description = "Fundamental package for array computing in Python" +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "numpy-1.24.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:179a7ef0889ab769cc03573b6217f54c8bd8e16cef80aad369e1e8185f994cd7"}, + {file = "numpy-1.24.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b09804ff570b907da323b3d762e74432fb07955701b17b08ff1b5ebaa8cfe6a9"}, + {file = "numpy-1.24.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1b739841821968798947d3afcefd386fa56da0caf97722a5de53e07c4ccedc7"}, + {file = "numpy-1.24.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e3463e6ac25313462e04aea3fb8a0a30fb906d5d300f58b3bc2c23da6a15398"}, + {file = "numpy-1.24.1-cp310-cp310-win32.whl", hash = "sha256:b31da69ed0c18be8b77bfce48d234e55d040793cebb25398e2a7d84199fbc7e2"}, + {file = "numpy-1.24.1-cp310-cp310-win_amd64.whl", hash = "sha256:b07b40f5fb4fa034120a5796288f24c1fe0e0580bbfff99897ba6267af42def2"}, + {file = "numpy-1.24.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7094891dcf79ccc6bc2a1f30428fa5edb1e6fb955411ffff3401fb4ea93780a8"}, + {file = "numpy-1.24.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:28e418681372520c992805bb723e29d69d6b7aa411065f48216d8329d02ba032"}, + {file = "numpy-1.24.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e274f0f6c7efd0d577744f52032fdd24344f11c5ae668fe8d01aac0422611df1"}, + {file = "numpy-1.24.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0044f7d944ee882400890f9ae955220d29b33d809a038923d88e4e01d652acd9"}, + {file = "numpy-1.24.1-cp311-cp311-win32.whl", hash = "sha256:442feb5e5bada8408e8fcd43f3360b78683ff12a4444670a7d9e9824c1817d36"}, + {file = "numpy-1.24.1-cp311-cp311-win_amd64.whl", hash = "sha256:de92efa737875329b052982e37bd4371d52cabf469f83e7b8be9bb7752d67e51"}, + {file = "numpy-1.24.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b162ac10ca38850510caf8ea33f89edcb7b0bb0dfa5592d59909419986b72407"}, + {file = "numpy-1.24.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:26089487086f2648944f17adaa1a97ca6aee57f513ba5f1c0b7ebdabbe2b9954"}, + {file = "numpy-1.24.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:caf65a396c0d1f9809596be2e444e3bd4190d86d5c1ce21f5fc4be60a3bc5b36"}, + {file = "numpy-1.24.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b0677a52f5d896e84414761531947c7a330d1adc07c3a4372262f25d84af7bf7"}, + {file = "numpy-1.24.1-cp38-cp38-win32.whl", hash = "sha256:dae46bed2cb79a58d6496ff6d8da1e3b95ba09afeca2e277628171ca99b99db1"}, + {file = "numpy-1.24.1-cp38-cp38-win_amd64.whl", hash = "sha256:6ec0c021cd9fe732e5bab6401adea5a409214ca5592cd92a114f7067febcba0c"}, + {file = "numpy-1.24.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:28bc9750ae1f75264ee0f10561709b1462d450a4808cd97c013046073ae64ab6"}, + {file = "numpy-1.24.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:84e789a085aabef2f36c0515f45e459f02f570c4b4c4c108ac1179c34d475ed7"}, + {file = "numpy-1.24.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e669fbdcdd1e945691079c2cae335f3e3a56554e06bbd45d7609a6cf568c700"}, + {file = "numpy-1.24.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef85cf1f693c88c1fd229ccd1055570cb41cdf4875873b7728b6301f12cd05bf"}, + {file = "numpy-1.24.1-cp39-cp39-win32.whl", hash = "sha256:87a118968fba001b248aac90e502c0b13606721b1343cdaddbc6e552e8dfb56f"}, + {file = "numpy-1.24.1-cp39-cp39-win_amd64.whl", hash = "sha256:ddc7ab52b322eb1e40521eb422c4e0a20716c271a306860979d450decbb51b8e"}, + {file = "numpy-1.24.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ed5fb71d79e771ec930566fae9c02626b939e37271ec285e9efaf1b5d4370e7d"}, + {file = "numpy-1.24.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad2925567f43643f51255220424c23d204024ed428afc5aad0f86f3ffc080086"}, + {file = "numpy-1.24.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:cfa1161c6ac8f92dea03d625c2d0c05e084668f4a06568b77a25a89111621566"}, + {file = "numpy-1.24.1.tar.gz", hash = "sha256:2386da9a471cc00a1f47845e27d916d5ec5346ae9696e01a8a34760858fe9dd2"}, +] + +[[package]] +name = "nvidia-cublas-cu11" +version = "11.10.3.66" +description = "CUBLAS native runtime libraries" +category = "main" +optional = false +python-versions = ">=3" +files = [ + {file = "nvidia_cublas_cu11-11.10.3.66-py3-none-manylinux1_x86_64.whl", hash = "sha256:d32e4d75f94ddfb93ea0a5dda08389bcc65d8916a25cb9f37ac89edaeed3bded"}, + {file = "nvidia_cublas_cu11-11.10.3.66-py3-none-win_amd64.whl", hash = "sha256:8ac17ba6ade3ed56ab898a036f9ae0756f1e81052a317bf98f8c6d18dc3ae49e"}, +] + +[package.dependencies] +setuptools = "*" +wheel = "*" + +[[package]] +name = "nvidia-cuda-nvrtc-cu11" +version = "11.7.99" +description = "NVRTC native runtime libraries" +category = "main" +optional = false +python-versions = ">=3" +files = [ + {file = "nvidia_cuda_nvrtc_cu11-11.7.99-2-py3-none-manylinux1_x86_64.whl", hash = "sha256:9f1562822ea264b7e34ed5930567e89242d266448e936b85bc97a3370feabb03"}, + {file = "nvidia_cuda_nvrtc_cu11-11.7.99-py3-none-manylinux1_x86_64.whl", hash = "sha256:f7d9610d9b7c331fa0da2d1b2858a4a8315e6d49765091d28711c8946e7425e7"}, + {file = "nvidia_cuda_nvrtc_cu11-11.7.99-py3-none-win_amd64.whl", hash = "sha256:f2effeb1309bdd1b3854fc9b17eaf997808f8b25968ce0c7070945c4265d64a3"}, +] + +[package.dependencies] +setuptools = "*" +wheel = "*" + +[[package]] +name = "nvidia-cuda-runtime-cu11" +version = "11.7.99" +description = "CUDA Runtime native Libraries" +category = "main" +optional = false +python-versions = ">=3" +files = [ + {file = "nvidia_cuda_runtime_cu11-11.7.99-py3-none-manylinux1_x86_64.whl", hash = "sha256:cc768314ae58d2641f07eac350f40f99dcb35719c4faff4bc458a7cd2b119e31"}, + {file = "nvidia_cuda_runtime_cu11-11.7.99-py3-none-win_amd64.whl", hash = "sha256:bc77fa59a7679310df9d5c70ab13c4e34c64ae2124dd1efd7e5474b71be125c7"}, +] + +[package.dependencies] +setuptools = "*" +wheel = "*" + +[[package]] +name = "nvidia-cudnn-cu11" +version = "8.5.0.96" +description = "cuDNN runtime libraries" +category = "main" +optional = false +python-versions = ">=3" +files = [ + {file = "nvidia_cudnn_cu11-8.5.0.96-2-py3-none-manylinux1_x86_64.whl", hash = "sha256:402f40adfc6f418f9dae9ab402e773cfed9beae52333f6d86ae3107a1b9527e7"}, + {file = "nvidia_cudnn_cu11-8.5.0.96-py3-none-manylinux1_x86_64.whl", hash = "sha256:71f8111eb830879ff2836db3cccf03bbd735df9b0d17cd93761732ac50a8a108"}, +] + +[package.dependencies] +setuptools = "*" +wheel = "*" + +[[package]] +name = "oauthlib" +version = "3.2.2" +description = "A generic, spec-compliant, thorough implementation of the OAuth request-signing logic" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "oauthlib-3.2.2-py3-none-any.whl", hash = "sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca"}, + {file = "oauthlib-3.2.2.tar.gz", hash = "sha256:9859c40929662bec5d64f34d01c99e093149682a3f38915dc0655d5a633dd918"}, +] + +[package.extras] +rsa = ["cryptography (>=3.0.0)"] +signals = ["blinker (>=1.4.0)"] +signedtoken = ["cryptography (>=3.0.0)", "pyjwt (>=2.0.0,<3)"] + +[[package]] +name = "opencv-python" +version = "4.7.0.68" +description = "Wrapper package for OpenCV python bindings." +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "opencv-python-4.7.0.68.tar.gz", hash = "sha256:9829e6efedde1d1b8419c5bd4d62d289ecbf44ae35b843c6da9e3cbcba1a9a8a"}, + {file = "opencv_python-4.7.0.68-cp37-abi3-macosx_10_13_x86_64.whl", hash = "sha256:abc6adfa8694f71a4caffa922b279bd9d96954a37eee40b147f613c64310b411"}, + {file = "opencv_python-4.7.0.68-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:86f4b60b9536948f16d2170ba3a9b22d3955a957dc61a9bc56e53692c6db2c7e"}, + {file = "opencv_python-4.7.0.68-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d1c993811f92ddd7919314ada7b9be1f23db1c73f1384915c834dee8549c0b9"}, + {file = "opencv_python-4.7.0.68-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a00e12546e5578f6bb7ed408c37fcfea533d74e9691cfaf40926f6b43295577"}, + {file = "opencv_python-4.7.0.68-cp37-abi3-win32.whl", hash = "sha256:e770e9f653a0e5e72b973adb8213fae2df4642730ba1faf31e73a54287a4d5d4"}, + {file = "opencv_python-4.7.0.68-cp37-abi3-win_amd64.whl", hash = "sha256:7a08f9d1f9dd52de63a7bb448ab7d6d4a1a85b767c2358501d968d1e4d95098d"}, +] + +[package.dependencies] +numpy = [ + {version = ">=1.21.2", markers = "python_version >= \"3.10\""}, + {version = ">=1.21.4", markers = "python_version >= \"3.10\" and platform_system == \"Darwin\""}, + {version = ">=1.19.3", markers = "python_version >= \"3.6\" and platform_system == \"Linux\" and platform_machine == \"aarch64\" or python_version >= \"3.9\""}, + {version = ">=1.17.0", markers = "python_version >= \"3.7\""}, + {version = ">=1.17.3", markers = "python_version >= \"3.8\""}, +] + +[[package]] +name = "packaging" +version = "23.0" +description = "Core utilities for Python packages" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "packaging-23.0-py3-none-any.whl", hash = "sha256:714ac14496c3e68c99c29b00845f7a2b85f3bb6f1078fd9f72fd20f0570002b2"}, + {file = "packaging-23.0.tar.gz", hash = "sha256:b6ad297f8907de0fa2fe1ccbd26fdaf387f5f47c7275fedf8cce89f99446cf97"}, +] + +[[package]] +name = "pandas" +version = "1.5.2" +description = "Powerful data structures for data analysis, time series, and statistics" +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pandas-1.5.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e9dbacd22555c2d47f262ef96bb4e30880e5956169741400af8b306bbb24a273"}, + {file = "pandas-1.5.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e2b83abd292194f350bb04e188f9379d36b8dfac24dd445d5c87575f3beaf789"}, + {file = "pandas-1.5.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2552bffc808641c6eb471e55aa6899fa002ac94e4eebfa9ec058649122db5824"}, + {file = "pandas-1.5.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fc87eac0541a7d24648a001d553406f4256e744d92df1df8ebe41829a915028"}, + {file = "pandas-1.5.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d0d8fd58df5d17ddb8c72a5075d87cd80d71b542571b5f78178fb067fa4e9c72"}, + {file = "pandas-1.5.2-cp310-cp310-win_amd64.whl", hash = "sha256:4aed257c7484d01c9a194d9a94758b37d3d751849c05a0050c087a358c41ad1f"}, + {file = "pandas-1.5.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:375262829c8c700c3e7cbb336810b94367b9c4889818bbd910d0ecb4e45dc261"}, + {file = "pandas-1.5.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc3cd122bea268998b79adebbb8343b735a5511ec14efb70a39e7acbc11ccbdc"}, + {file = "pandas-1.5.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b4f5a82afa4f1ff482ab8ded2ae8a453a2cdfde2001567b3ca24a4c5c5ca0db3"}, + {file = "pandas-1.5.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8092a368d3eb7116e270525329a3e5c15ae796ccdf7ccb17839a73b4f5084a39"}, + {file = "pandas-1.5.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6257b314fc14958f8122779e5a1557517b0f8e500cfb2bd53fa1f75a8ad0af2"}, + {file = "pandas-1.5.2-cp311-cp311-win_amd64.whl", hash = "sha256:82ae615826da838a8e5d4d630eb70c993ab8636f0eff13cb28aafc4291b632b5"}, + {file = "pandas-1.5.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:457d8c3d42314ff47cc2d6c54f8fc0d23954b47977b2caed09cd9635cb75388b"}, + {file = "pandas-1.5.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c009a92e81ce836212ce7aa98b219db7961a8b95999b97af566b8dc8c33e9519"}, + {file = "pandas-1.5.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:71f510b0efe1629bf2f7c0eadb1ff0b9cf611e87b73cd017e6b7d6adb40e2b3a"}, + {file = "pandas-1.5.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a40dd1e9f22e01e66ed534d6a965eb99546b41d4d52dbdb66565608fde48203f"}, + {file = "pandas-1.5.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ae7e989f12628f41e804847a8cc2943d362440132919a69429d4dea1f164da0"}, + {file = "pandas-1.5.2-cp38-cp38-win32.whl", hash = "sha256:530948945e7b6c95e6fa7aa4be2be25764af53fba93fe76d912e35d1c9ee46f5"}, + {file = "pandas-1.5.2-cp38-cp38-win_amd64.whl", hash = "sha256:73f219fdc1777cf3c45fde7f0708732ec6950dfc598afc50588d0d285fddaefc"}, + {file = "pandas-1.5.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:9608000a5a45f663be6af5c70c3cbe634fa19243e720eb380c0d378666bc7702"}, + {file = "pandas-1.5.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:315e19a3e5c2ab47a67467fc0362cb36c7c60a93b6457f675d7d9615edad2ebe"}, + {file = "pandas-1.5.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e18bc3764cbb5e118be139b3b611bc3fbc5d3be42a7e827d1096f46087b395eb"}, + {file = "pandas-1.5.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0183cb04a057cc38fde5244909fca9826d5d57c4a5b7390c0cc3fa7acd9fa883"}, + {file = "pandas-1.5.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:344021ed3e639e017b452aa8f5f6bf38a8806f5852e217a7594417fb9bbfa00e"}, + {file = "pandas-1.5.2-cp39-cp39-win32.whl", hash = "sha256:e7469271497960b6a781eaa930cba8af400dd59b62ec9ca2f4d31a19f2f91090"}, + {file = "pandas-1.5.2-cp39-cp39-win_amd64.whl", hash = "sha256:c218796d59d5abd8780170c937b812c9637e84c32f8271bbf9845970f8c1351f"}, + {file = "pandas-1.5.2.tar.gz", hash = "sha256:220b98d15cee0b2cd839a6358bd1f273d0356bf964c1a1aeb32d47db0215488b"}, +] + +[package.dependencies] +numpy = {version = ">=1.21.0", markers = "python_version >= \"3.10\""} +python-dateutil = ">=2.8.1" +pytz = ">=2020.1" + +[package.extras] +test = ["hypothesis (>=5.5.3)", "pytest (>=6.0)", "pytest-xdist (>=1.31)"] + +[[package]] +name = "pandocfilters" +version = "1.5.0" +description = "Utilities for writing pandoc filters in python" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "pandocfilters-1.5.0-py2.py3-none-any.whl", hash = "sha256:33aae3f25fd1a026079f5d27bdd52496f0e0803b3469282162bafdcbdf6ef14f"}, + {file = "pandocfilters-1.5.0.tar.gz", hash = "sha256:0b679503337d233b4339a817bfc8c50064e2eff681314376a47cb582305a7a38"}, +] + +[[package]] +name = "parso" +version = "0.8.3" +description = "A Python Parser" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "parso-0.8.3-py2.py3-none-any.whl", hash = "sha256:c001d4636cd3aecdaf33cbb40aebb59b094be2a74c556778ef5576c175e19e75"}, + {file = "parso-0.8.3.tar.gz", hash = "sha256:8c07be290bb59f03588915921e29e8a50002acaf2cdc5fa0e0114f91709fafa0"}, +] + +[package.extras] +qa = ["flake8 (==3.8.3)", "mypy (==0.782)"] +testing = ["docopt", "pytest (<6.0.0)"] + +[[package]] +name = "pastel" +version = "0.2.1" +description = "Bring colors to your terminal." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "pastel-0.2.1-py2.py3-none-any.whl", hash = "sha256:4349225fcdf6c2bb34d483e523475de5bb04a5c10ef711263452cb37d7dd4364"}, + {file = "pastel-0.2.1.tar.gz", hash = "sha256:e6581ac04e973cac858828c6202c1e1e81fee1dc7de7683f3e1ffe0bfd8a573d"}, +] + +[[package]] +name = "pathtools" +version = "0.1.2" +description = "File system general utilities" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "pathtools-0.1.2.tar.gz", hash = "sha256:7c35c5421a39bb82e58018febd90e3b6e5db34c5443aaaf742b3f33d4655f1c0"}, +] + +[[package]] +name = "pexpect" +version = "4.8.0" +description = "Pexpect allows easy control of interactive console applications." +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "pexpect-4.8.0-py2.py3-none-any.whl", hash = "sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937"}, + {file = "pexpect-4.8.0.tar.gz", hash = "sha256:fc65a43959d153d0114afe13997d439c22823a27cefceb5ff35c2178c6784c0c"}, +] + +[package.dependencies] +ptyprocess = ">=0.5" + +[[package]] +name = "pickleshare" +version = "0.7.5" +description = "Tiny 'shelve'-like database with concurrency support" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "pickleshare-0.7.5-py2.py3-none-any.whl", hash = "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"}, + {file = "pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"}, +] + +[[package]] +name = "pillow" +version = "9.4.0" +description = "Python Imaging Library (Fork)" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "Pillow-9.4.0-1-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:1b4b4e9dda4f4e4c4e6896f93e84a8f0bcca3b059de9ddf67dac3c334b1195e1"}, + {file = "Pillow-9.4.0-1-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:fb5c1ad6bad98c57482236a21bf985ab0ef42bd51f7ad4e4538e89a997624e12"}, + {file = "Pillow-9.4.0-1-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:f0caf4a5dcf610d96c3bd32932bfac8aee61c96e60481c2a0ea58da435e25acd"}, + {file = "Pillow-9.4.0-1-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:3f4cc516e0b264c8d4ccd6b6cbc69a07c6d582d8337df79be1e15a5056b258c9"}, + {file = "Pillow-9.4.0-1-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:b8c2f6eb0df979ee99433d8b3f6d193d9590f735cf12274c108bd954e30ca858"}, + {file = "Pillow-9.4.0-1-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:b70756ec9417c34e097f987b4d8c510975216ad26ba6e57ccb53bc758f490dab"}, + {file = "Pillow-9.4.0-1-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:43521ce2c4b865d385e78579a082b6ad1166ebed2b1a2293c3be1d68dd7ca3b9"}, + {file = "Pillow-9.4.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:2968c58feca624bb6c8502f9564dd187d0e1389964898f5e9e1fbc8533169157"}, + {file = "Pillow-9.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c5c1362c14aee73f50143d74389b2c158707b4abce2cb055b7ad37ce60738d47"}, + {file = "Pillow-9.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd752c5ff1b4a870b7661234694f24b1d2b9076b8bf337321a814c612665f343"}, + {file = "Pillow-9.4.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a3049a10261d7f2b6514d35bbb7a4dfc3ece4c4de14ef5876c4b7a23a0e566d"}, + {file = "Pillow-9.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:16a8df99701f9095bea8a6c4b3197da105df6f74e6176c5b410bc2df2fd29a57"}, + {file = "Pillow-9.4.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:94cdff45173b1919350601f82d61365e792895e3c3a3443cf99819e6fbf717a5"}, + {file = "Pillow-9.4.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:ed3e4b4e1e6de75fdc16d3259098de7c6571b1a6cc863b1a49e7d3d53e036070"}, + {file = "Pillow-9.4.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d5b2f8a31bd43e0f18172d8ac82347c8f37ef3e0b414431157718aa234991b28"}, + {file = "Pillow-9.4.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:09b89ddc95c248ee788328528e6a2996e09eaccddeeb82a5356e92645733be35"}, + {file = "Pillow-9.4.0-cp310-cp310-win32.whl", hash = "sha256:f09598b416ba39a8f489c124447b007fe865f786a89dbfa48bb5cf395693132a"}, + {file = "Pillow-9.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:f6e78171be3fb7941f9910ea15b4b14ec27725865a73c15277bc39f5ca4f8391"}, + {file = "Pillow-9.4.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:3fa1284762aacca6dc97474ee9c16f83990b8eeb6697f2ba17140d54b453e133"}, + {file = "Pillow-9.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:eaef5d2de3c7e9b21f1e762f289d17b726c2239a42b11e25446abf82b26ac132"}, + {file = "Pillow-9.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a4dfdae195335abb4e89cc9762b2edc524f3c6e80d647a9a81bf81e17e3fb6f0"}, + {file = "Pillow-9.4.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6abfb51a82e919e3933eb137e17c4ae9c0475a25508ea88993bb59faf82f3b35"}, + {file = "Pillow-9.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:451f10ef963918e65b8869e17d67db5e2f4ab40e716ee6ce7129b0cde2876eab"}, + {file = "Pillow-9.4.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:6663977496d616b618b6cfa43ec86e479ee62b942e1da76a2c3daa1c75933ef4"}, + {file = "Pillow-9.4.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:60e7da3a3ad1812c128750fc1bc14a7ceeb8d29f77e0a2356a8fb2aa8925287d"}, + {file = "Pillow-9.4.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:19005a8e58b7c1796bc0167862b1f54a64d3b44ee5d48152b06bb861458bc0f8"}, + {file = "Pillow-9.4.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f715c32e774a60a337b2bb8ad9839b4abf75b267a0f18806f6f4f5f1688c4b5a"}, + {file = "Pillow-9.4.0-cp311-cp311-win32.whl", hash = "sha256:b222090c455d6d1a64e6b7bb5f4035c4dff479e22455c9eaa1bdd4c75b52c80c"}, + {file = "Pillow-9.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:ba6612b6548220ff5e9df85261bddc811a057b0b465a1226b39bfb8550616aee"}, + {file = "Pillow-9.4.0-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:5f532a2ad4d174eb73494e7397988e22bf427f91acc8e6ebf5bb10597b49c493"}, + {file = "Pillow-9.4.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5dd5a9c3091a0f414a963d427f920368e2b6a4c2f7527fdd82cde8ef0bc7a327"}, + {file = "Pillow-9.4.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ef21af928e807f10bf4141cad4746eee692a0dd3ff56cfb25fce076ec3cc8abe"}, + {file = "Pillow-9.4.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:847b114580c5cc9ebaf216dd8c8dbc6b00a3b7ab0131e173d7120e6deade1f57"}, + {file = "Pillow-9.4.0-cp37-cp37m-manylinux_2_28_aarch64.whl", hash = "sha256:653d7fb2df65efefbcbf81ef5fe5e5be931f1ee4332c2893ca638c9b11a409c4"}, + {file = "Pillow-9.4.0-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:46f39cab8bbf4a384ba7cb0bc8bae7b7062b6a11cfac1ca4bc144dea90d4a9f5"}, + {file = "Pillow-9.4.0-cp37-cp37m-win32.whl", hash = "sha256:7ac7594397698f77bce84382929747130765f66406dc2cd8b4ab4da68ade4c6e"}, + {file = "Pillow-9.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:46c259e87199041583658457372a183636ae8cd56dbf3f0755e0f376a7f9d0e6"}, + {file = "Pillow-9.4.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:0e51f608da093e5d9038c592b5b575cadc12fd748af1479b5e858045fff955a9"}, + {file = "Pillow-9.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:765cb54c0b8724a7c12c55146ae4647e0274a839fb6de7bcba841e04298e1011"}, + {file = "Pillow-9.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:519e14e2c49fcf7616d6d2cfc5c70adae95682ae20f0395e9280db85e8d6c4df"}, + {file = "Pillow-9.4.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d197df5489004db87d90b918033edbeee0bd6df3848a204bca3ff0a903bef837"}, + {file = "Pillow-9.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0845adc64fe9886db00f5ab68c4a8cd933ab749a87747555cec1c95acea64b0b"}, + {file = "Pillow-9.4.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:e1339790c083c5a4de48f688b4841f18df839eb3c9584a770cbd818b33e26d5d"}, + {file = "Pillow-9.4.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:a96e6e23f2b79433390273eaf8cc94fec9c6370842e577ab10dabdcc7ea0a66b"}, + {file = "Pillow-9.4.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:7cfc287da09f9d2a7ec146ee4d72d6ea1342e770d975e49a8621bf54eaa8f30f"}, + {file = "Pillow-9.4.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d7081c084ceb58278dd3cf81f836bc818978c0ccc770cbbb202125ddabec6628"}, + {file = "Pillow-9.4.0-cp38-cp38-win32.whl", hash = "sha256:df41112ccce5d47770a0c13651479fbcd8793f34232a2dd9faeccb75eb5d0d0d"}, + {file = "Pillow-9.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:7a21222644ab69ddd9967cfe6f2bb420b460dae4289c9d40ff9a4896e7c35c9a"}, + {file = "Pillow-9.4.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:0f3269304c1a7ce82f1759c12ce731ef9b6e95b6df829dccd9fe42912cc48569"}, + {file = "Pillow-9.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cb362e3b0976dc994857391b776ddaa8c13c28a16f80ac6522c23d5257156bed"}, + {file = "Pillow-9.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a2e0f87144fcbbe54297cae708c5e7f9da21a4646523456b00cc956bd4c65815"}, + {file = "Pillow-9.4.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:28676836c7796805914b76b1837a40f76827ee0d5398f72f7dcc634bae7c6264"}, + {file = "Pillow-9.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0884ba7b515163a1a05440a138adeb722b8a6ae2c2b33aea93ea3118dd3a899e"}, + {file = "Pillow-9.4.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:53dcb50fbdc3fb2c55431a9b30caeb2f7027fcd2aeb501459464f0214200a503"}, + {file = "Pillow-9.4.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:e8c5cf126889a4de385c02a2c3d3aba4b00f70234bfddae82a5eaa3ee6d5e3e6"}, + {file = "Pillow-9.4.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6c6b1389ed66cdd174d040105123a5a1bc91d0aa7059c7261d20e583b6d8cbd2"}, + {file = "Pillow-9.4.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0dd4c681b82214b36273c18ca7ee87065a50e013112eea7d78c7a1b89a739153"}, + {file = "Pillow-9.4.0-cp39-cp39-win32.whl", hash = "sha256:6d9dfb9959a3b0039ee06c1a1a90dc23bac3b430842dcb97908ddde05870601c"}, + {file = "Pillow-9.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:54614444887e0d3043557d9dbc697dbb16cfb5a35d672b7a0fcc1ed0cf1c600b"}, + {file = "Pillow-9.4.0-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:b9b752ab91e78234941e44abdecc07f1f0d8f51fb62941d32995b8161f68cfe5"}, + {file = "Pillow-9.4.0-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d3b56206244dc8711f7e8b7d6cad4663917cd5b2d950799425076681e8766286"}, + {file = "Pillow-9.4.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aabdab8ec1e7ca7f1434d042bf8b1e92056245fb179790dc97ed040361f16bfd"}, + {file = "Pillow-9.4.0-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:db74f5562c09953b2c5f8ec4b7dfd3f5421f31811e97d1dbc0a7c93d6e3a24df"}, + {file = "Pillow-9.4.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:e9d7747847c53a16a729b6ee5e737cf170f7a16611c143d95aa60a109a59c336"}, + {file = "Pillow-9.4.0-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:b52ff4f4e002f828ea6483faf4c4e8deea8d743cf801b74910243c58acc6eda3"}, + {file = "Pillow-9.4.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:575d8912dca808edd9acd6f7795199332696d3469665ef26163cd090fa1f8bfa"}, + {file = "Pillow-9.4.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3c4ed2ff6760e98d262e0cc9c9a7f7b8a9f61aa4d47c58835cdaf7b0b8811bb"}, + {file = "Pillow-9.4.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e621b0246192d3b9cb1dc62c78cfa4c6f6d2ddc0ec207d43c0dedecb914f152a"}, + {file = "Pillow-9.4.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:8f127e7b028900421cad64f51f75c051b628db17fb00e099eb148761eed598c9"}, + {file = "Pillow-9.4.0.tar.gz", hash = "sha256:a1c2d7780448eb93fbcc3789bf3916aa5720d942e37945f4056680317f1cd23e"}, +] + +[package.extras] +docs = ["furo", "olefile", "sphinx (>=2.4)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-issues (>=3.0.1)", "sphinx-removed-in", "sphinxext-opengraph"] +tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"] + +[[package]] +name = "pkginfo" +version = "1.9.6" +description = "Query metadata from sdists / bdists / installed packages." +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pkginfo-1.9.6-py3-none-any.whl", hash = "sha256:4b7a555a6d5a22169fcc9cf7bfd78d296b0361adad412a346c1226849af5e546"}, + {file = "pkginfo-1.9.6.tar.gz", hash = "sha256:8fd5896e8718a4372f0ea9cc9d96f6417c9b986e23a4d116dda26b62cc29d046"}, +] + +[package.extras] +testing = ["pytest", "pytest-cov"] + +[[package]] +name = "platformdirs" +version = "2.6.2" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "platformdirs-2.6.2-py3-none-any.whl", hash = "sha256:83c8f6d04389165de7c9b6f0c682439697887bca0aa2f1c87ef1826be3584490"}, + {file = "platformdirs-2.6.2.tar.gz", hash = "sha256:e1fea1fe471b9ff8332e229df3cb7de4f53eeea4998d3b6bfff542115e998bd2"}, +] + +[package.extras] +docs = ["furo (>=2022.12.7)", "proselint (>=0.13)", "sphinx (>=5.3)", "sphinx-autodoc-typehints (>=1.19.5)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.2.2)", "pytest (>=7.2)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"] + +[[package]] +name = "prometheus-client" +version = "0.15.0" +description = "Python client for the Prometheus monitoring system." +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "prometheus_client-0.15.0-py3-none-any.whl", hash = "sha256:db7c05cbd13a0f79975592d112320f2605a325969b270a94b71dcabc47b931d2"}, + {file = "prometheus_client-0.15.0.tar.gz", hash = "sha256:be26aa452490cfcf6da953f9436e95a9f2b4d578ca80094b4458930e5f584ab1"}, +] + +[package.extras] +twisted = ["twisted"] + +[[package]] +name = "prompt-toolkit" +version = "3.0.36" +description = "Library for building powerful interactive command lines in Python" +category = "main" +optional = false +python-versions = ">=3.6.2" +files = [ + {file = "prompt_toolkit-3.0.36-py3-none-any.whl", hash = "sha256:aa64ad242a462c5ff0363a7b9cfe696c20d55d9fc60c11fd8e632d064804d305"}, + {file = "prompt_toolkit-3.0.36.tar.gz", hash = "sha256:3e163f254bef5a03b146397d7c1963bd3e2812f0964bb9a24e6ec761fd28db63"}, +] + +[package.dependencies] +wcwidth = "*" + +[[package]] +name = "protobuf" +version = "3.20.3" +description = "Protocol Buffers" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "protobuf-3.20.3-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:f4bd856d702e5b0d96a00ec6b307b0f51c1982c2bf9c0052cf9019e9a544ba99"}, + {file = "protobuf-3.20.3-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9aae4406ea63d825636cc11ffb34ad3379335803216ee3a856787bcf5ccc751e"}, + {file = "protobuf-3.20.3-cp310-cp310-win32.whl", hash = "sha256:28545383d61f55b57cf4df63eebd9827754fd2dc25f80c5253f9184235db242c"}, + {file = "protobuf-3.20.3-cp310-cp310-win_amd64.whl", hash = "sha256:67a3598f0a2dcbc58d02dd1928544e7d88f764b47d4a286202913f0b2801c2e7"}, + {file = "protobuf-3.20.3-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:899dc660cd599d7352d6f10d83c95df430a38b410c1b66b407a6b29265d66469"}, + {file = "protobuf-3.20.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e64857f395505ebf3d2569935506ae0dfc4a15cb80dc25261176c784662cdcc4"}, + {file = "protobuf-3.20.3-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:d9e4432ff660d67d775c66ac42a67cf2453c27cb4d738fc22cb53b5d84c135d4"}, + {file = "protobuf-3.20.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:74480f79a023f90dc6e18febbf7b8bac7508420f2006fabd512013c0c238f454"}, + {file = "protobuf-3.20.3-cp37-cp37m-win32.whl", hash = "sha256:b6cc7ba72a8850621bfec987cb72623e703b7fe2b9127a161ce61e61558ad905"}, + {file = "protobuf-3.20.3-cp37-cp37m-win_amd64.whl", hash = "sha256:8c0c984a1b8fef4086329ff8dd19ac77576b384079247c770f29cc8ce3afa06c"}, + {file = "protobuf-3.20.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:de78575669dddf6099a8a0f46a27e82a1783c557ccc38ee620ed8cc96d3be7d7"}, + {file = "protobuf-3.20.3-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:f4c42102bc82a51108e449cbb32b19b180022941c727bac0cfd50170341f16ee"}, + {file = "protobuf-3.20.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:44246bab5dd4b7fbd3c0c80b6f16686808fab0e4aca819ade6e8d294a29c7050"}, + {file = "protobuf-3.20.3-cp38-cp38-win32.whl", hash = "sha256:c02ce36ec760252242a33967d51c289fd0e1c0e6e5cc9397e2279177716add86"}, + {file = "protobuf-3.20.3-cp38-cp38-win_amd64.whl", hash = "sha256:447d43819997825d4e71bf5769d869b968ce96848b6479397e29fc24c4a5dfe9"}, + {file = "protobuf-3.20.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:398a9e0c3eaceb34ec1aee71894ca3299605fa8e761544934378bbc6c97de23b"}, + {file = "protobuf-3.20.3-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:bf01b5720be110540be4286e791db73f84a2b721072a3711efff6c324cdf074b"}, + {file = "protobuf-3.20.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:daa564862dd0d39c00f8086f88700fdbe8bc717e993a21e90711acfed02f2402"}, + {file = "protobuf-3.20.3-cp39-cp39-win32.whl", hash = "sha256:819559cafa1a373b7096a482b504ae8a857c89593cf3a25af743ac9ecbd23480"}, + {file = "protobuf-3.20.3-cp39-cp39-win_amd64.whl", hash = "sha256:03038ac1cfbc41aa21f6afcbcd357281d7521b4157926f30ebecc8d4ea59dcb7"}, + {file = "protobuf-3.20.3-py2.py3-none-any.whl", hash = "sha256:a7ca6d488aa8ff7f329d4c545b2dbad8ac31464f1d8b1c87ad1346717731e4db"}, + {file = "protobuf-3.20.3.tar.gz", hash = "sha256:2e3427429c9cffebf259491be0af70189607f365c2f41c7c3764af6f337105f2"}, +] + +[[package]] +name = "psutil" +version = "5.9.4" +description = "Cross-platform lib for process and system monitoring in Python." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "psutil-5.9.4-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:c1ca331af862803a42677c120aff8a814a804e09832f166f226bfd22b56feee8"}, + {file = "psutil-5.9.4-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:68908971daf802203f3d37e78d3f8831b6d1014864d7a85937941bb35f09aefe"}, + {file = "psutil-5.9.4-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:3ff89f9b835100a825b14c2808a106b6fdcc4b15483141482a12c725e7f78549"}, + {file = "psutil-5.9.4-cp27-cp27m-win32.whl", hash = "sha256:852dd5d9f8a47169fe62fd4a971aa07859476c2ba22c2254d4a1baa4e10b95ad"}, + {file = "psutil-5.9.4-cp27-cp27m-win_amd64.whl", hash = "sha256:9120cd39dca5c5e1c54b59a41d205023d436799b1c8c4d3ff71af18535728e94"}, + {file = "psutil-5.9.4-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:6b92c532979bafc2df23ddc785ed116fced1f492ad90a6830cf24f4d1ea27d24"}, + {file = "psutil-5.9.4-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:efeae04f9516907be44904cc7ce08defb6b665128992a56957abc9b61dca94b7"}, + {file = "psutil-5.9.4-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:54d5b184728298f2ca8567bf83c422b706200bcbbfafdc06718264f9393cfeb7"}, + {file = "psutil-5.9.4-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:16653106f3b59386ffe10e0bad3bb6299e169d5327d3f187614b1cb8f24cf2e1"}, + {file = "psutil-5.9.4-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54c0d3d8e0078b7666984e11b12b88af2db11d11249a8ac8920dd5ef68a66e08"}, + {file = "psutil-5.9.4-cp36-abi3-win32.whl", hash = "sha256:149555f59a69b33f056ba1c4eb22bb7bf24332ce631c44a319cec09f876aaeff"}, + {file = "psutil-5.9.4-cp36-abi3-win_amd64.whl", hash = "sha256:fd8522436a6ada7b4aad6638662966de0d61d241cb821239b2ae7013d41a43d4"}, + {file = "psutil-5.9.4-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:6001c809253a29599bc0dfd5179d9f8a5779f9dffea1da0f13c53ee568115e1e"}, + {file = "psutil-5.9.4.tar.gz", hash = "sha256:3d7f9739eb435d4b1338944abe23f49584bde5395f27487d2ee25ad9a8774a62"}, +] + +[package.extras] +test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"] + +[[package]] +name = "ptyprocess" +version = "0.7.0" +description = "Run a subprocess in a pseudo terminal" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, + {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, +] + +[[package]] +name = "pure-eval" +version = "0.2.2" +description = "Safely evaluate AST nodes without side effects" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "pure_eval-0.2.2-py3-none-any.whl", hash = "sha256:01eaab343580944bc56080ebe0a674b39ec44a945e6d09ba7db3cb8cec289350"}, + {file = "pure_eval-0.2.2.tar.gz", hash = "sha256:2b45320af6dfaa1750f543d714b6d1c520a1688dec6fd24d339063ce0aaa9ac3"}, +] + +[package.extras] +tests = ["pytest"] + +[[package]] +name = "pyasn1" +version = "0.4.8" +description = "ASN.1 types and codecs" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "pyasn1-0.4.8-py2.py3-none-any.whl", hash = "sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d"}, + {file = "pyasn1-0.4.8.tar.gz", hash = "sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba"}, +] + +[[package]] +name = "pyasn1-modules" +version = "0.2.8" +description = "A collection of ASN.1-based protocols modules." +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "pyasn1-modules-0.2.8.tar.gz", hash = "sha256:905f84c712230b2c592c19470d3ca8d552de726050d1d1716282a1f6146be65e"}, + {file = "pyasn1_modules-0.2.8-py2.py3-none-any.whl", hash = "sha256:a50b808ffeb97cb3601dd25981f6b016cbb3d31fbf57a8b8a87428e6158d0c74"}, +] + +[package.dependencies] +pyasn1 = ">=0.4.6,<0.5.0" + +[[package]] +name = "pybullet" +version = "3.2.5" +description = "Official Python Interface for the Bullet Physics SDK specialized for Robotics Simulation and Reinforcement Learning" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "pybullet-3.2.5-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:4970aec0dd968924f6b1820655a20f80650da2f85ba38b641937c9701a8a2b14"}, + {file = "pybullet-3.2.5-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:b64e4523a11d03729035e0a5baa0ce4d2ca58de8d0a242c0b91e8253781b24c4"}, + {file = "pybullet-3.2.5-cp35-cp35m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:49e80fd708a3ffd1d0dac3149e13852bd59cca056bb328bf35b25ea26a8bf504"}, + {file = "pybullet-3.2.5-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:56456b7b53ab00f33d52a3eb96fb0d7b4b8e16f21987d727b34baecc2019702f"}, + {file = "pybullet-3.2.5-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:3e22fdb949d0a67e18cc3e248d6199ff788704c68c3edbfc3b5c02fc58f52f9a"}, + {file = "pybullet-3.2.5-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:3f9c4289f1773b55915f4efb7514b088539d59b4a082465d68ee7caac11355d1"}, + {file = "pybullet-3.2.5-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:9adcaa00de674a02549949f6f8d51b485bd7a23fbc87a1defb2067e1364f8202"}, + {file = "pybullet-3.2.5.tar.gz", hash = "sha256:1bcb9afb87a086be1b2de18f084d1fdab8194da1bf71f264743ca26baa39c351"}, +] + +[[package]] +name = "pycparser" +version = "2.21" +description = "C parser in Python" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, + {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, +] + +[[package]] +name = "pydantic" +version = "1.10.4" +description = "Data validation and settings management using python type hints" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pydantic-1.10.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b5635de53e6686fe7a44b5cf25fcc419a0d5e5c1a1efe73d49d48fe7586db854"}, + {file = "pydantic-1.10.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6dc1cc241440ed7ca9ab59d9929075445da6b7c94ced281b3dd4cfe6c8cff817"}, + {file = "pydantic-1.10.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51bdeb10d2db0f288e71d49c9cefa609bca271720ecd0c58009bd7504a0c464c"}, + {file = "pydantic-1.10.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:78cec42b95dbb500a1f7120bdf95c401f6abb616bbe8785ef09887306792e66e"}, + {file = "pydantic-1.10.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8775d4ef5e7299a2f4699501077a0defdaac5b6c4321173bcb0f3c496fbadf85"}, + {file = "pydantic-1.10.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:572066051eeac73d23f95ba9a71349c42a3e05999d0ee1572b7860235b850cc6"}, + {file = "pydantic-1.10.4-cp310-cp310-win_amd64.whl", hash = "sha256:7feb6a2d401f4d6863050f58325b8d99c1e56f4512d98b11ac64ad1751dc647d"}, + {file = "pydantic-1.10.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:39f4a73e5342b25c2959529f07f026ef58147249f9b7431e1ba8414a36761f53"}, + {file = "pydantic-1.10.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:983e720704431a6573d626b00662eb78a07148c9115129f9b4351091ec95ecc3"}, + {file = "pydantic-1.10.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75d52162fe6b2b55964fbb0af2ee58e99791a3138588c482572bb6087953113a"}, + {file = "pydantic-1.10.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fdf8d759ef326962b4678d89e275ffc55b7ce59d917d9f72233762061fd04a2d"}, + {file = "pydantic-1.10.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:05a81b006be15655b2a1bae5faa4280cf7c81d0e09fcb49b342ebf826abe5a72"}, + {file = "pydantic-1.10.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d88c4c0e5c5dfd05092a4b271282ef0588e5f4aaf345778056fc5259ba098857"}, + {file = "pydantic-1.10.4-cp311-cp311-win_amd64.whl", hash = "sha256:6a05a9db1ef5be0fe63e988f9617ca2551013f55000289c671f71ec16f4985e3"}, + {file = "pydantic-1.10.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:887ca463c3bc47103c123bc06919c86720e80e1214aab79e9b779cda0ff92a00"}, + {file = "pydantic-1.10.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdf88ab63c3ee282c76d652fc86518aacb737ff35796023fae56a65ced1a5978"}, + {file = "pydantic-1.10.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a48f1953c4a1d9bd0b5167ac50da9a79f6072c63c4cef4cf2a3736994903583e"}, + {file = "pydantic-1.10.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:a9f2de23bec87ff306aef658384b02aa7c32389766af3c5dee9ce33e80222dfa"}, + {file = "pydantic-1.10.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:cd8702c5142afda03dc2b1ee6bc358b62b3735b2cce53fc77b31ca9f728e4bc8"}, + {file = "pydantic-1.10.4-cp37-cp37m-win_amd64.whl", hash = "sha256:6e7124d6855b2780611d9f5e1e145e86667eaa3bd9459192c8dc1a097f5e9903"}, + {file = "pydantic-1.10.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b53e1d41e97063d51a02821b80538053ee4608b9a181c1005441f1673c55423"}, + {file = "pydantic-1.10.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:55b1625899acd33229c4352ce0ae54038529b412bd51c4915349b49ca575258f"}, + {file = "pydantic-1.10.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:301d626a59edbe5dfb48fcae245896379a450d04baeed50ef40d8199f2733b06"}, + {file = "pydantic-1.10.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b6f9d649892a6f54a39ed56b8dfd5e08b5f3be5f893da430bed76975f3735d15"}, + {file = "pydantic-1.10.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:d7b5a3821225f5c43496c324b0d6875fde910a1c2933d726a743ce328fbb2a8c"}, + {file = "pydantic-1.10.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f2f7eb6273dd12472d7f218e1fef6f7c7c2f00ac2e1ecde4db8824c457300416"}, + {file = "pydantic-1.10.4-cp38-cp38-win_amd64.whl", hash = "sha256:4b05697738e7d2040696b0a66d9f0a10bec0efa1883ca75ee9e55baf511909d6"}, + {file = "pydantic-1.10.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a9a6747cac06c2beb466064dda999a13176b23535e4c496c9d48e6406f92d42d"}, + {file = "pydantic-1.10.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:eb992a1ef739cc7b543576337bebfc62c0e6567434e522e97291b251a41dad7f"}, + {file = "pydantic-1.10.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:990406d226dea0e8f25f643b370224771878142155b879784ce89f633541a024"}, + {file = "pydantic-1.10.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e82a6d37a95e0b1b42b82ab340ada3963aea1317fd7f888bb6b9dfbf4fff57c"}, + {file = "pydantic-1.10.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9193d4f4ee8feca58bc56c8306bcb820f5c7905fd919e0750acdeeeef0615b28"}, + {file = "pydantic-1.10.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2b3ce5f16deb45c472dde1a0ee05619298c864a20cded09c4edd820e1454129f"}, + {file = "pydantic-1.10.4-cp39-cp39-win_amd64.whl", hash = "sha256:9cbdc268a62d9a98c56e2452d6c41c0263d64a2009aac69246486f01b4f594c4"}, + {file = "pydantic-1.10.4-py3-none-any.whl", hash = "sha256:4948f264678c703f3877d1c8877c4e3b2e12e549c57795107f08cf70c6ec7774"}, + {file = "pydantic-1.10.4.tar.gz", hash = "sha256:b9a3859f24eb4e097502a3be1fb4b2abb79b6103dd9e2e0edb70613a4459a648"}, +] + +[package.dependencies] +typing-extensions = ">=4.2.0" + +[package.extras] +dotenv = ["python-dotenv (>=0.10.4)"] +email = ["email-validator (>=1.0.3)"] + +[[package]] +name = "pyglet" +version = "1.5.27" +description = "Cross-platform windowing and multimedia library" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "pyglet-1.5.27-py3-none-any.whl", hash = "sha256:7ce2eb5a299cda92fcc099f533520cdaa2f157a175d6c1442a2ae4d769284d2d"}, + {file = "pyglet-1.5.27.zip", hash = "sha256:4d00e067451f3b10fd51b69764fddab65444372a2da344ee2b35f0a8e6ebf005"}, +] + +[[package]] +name = "pygments" +version = "2.14.0" +description = "Pygments is a syntax highlighting package written in Python." +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "Pygments-2.14.0-py3-none-any.whl", hash = "sha256:fa7bd7bd2771287c0de303af8bfdfc731f51bd2c6a47ab69d117138893b82717"}, + {file = "Pygments-2.14.0.tar.gz", hash = "sha256:b3ed06a9e8ac9a9aae5a6f5dbe78a8a58655d17b43b93c078f094ddc476ae297"}, +] + +[package.extras] +plugins = ["importlib-metadata"] + +[[package]] +name = "pylev" +version = "1.4.0" +description = "A pure Python Levenshtein implementation that's not freaking GPL'd." +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "pylev-1.4.0-py2.py3-none-any.whl", hash = "sha256:7b2e2aa7b00e05bb3f7650eb506fc89f474f70493271a35c242d9a92188ad3dd"}, + {file = "pylev-1.4.0.tar.gz", hash = "sha256:9e77e941042ad3a4cc305dcdf2b2dec1aec2fbe3dd9015d2698ad02b173006d1"}, +] + +[[package]] +name = "pyparsing" +version = "3.0.9" +description = "pyparsing module - Classes and methods to define and execute parsing grammars" +category = "main" +optional = false +python-versions = ">=3.6.8" +files = [ + {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"}, + {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"}, +] + +[package.extras] +diagrams = ["jinja2", "railroad-diagrams"] + +[[package]] +name = "pyrsistent" +version = "0.19.3" +description = "Persistent/Functional/Immutable data structures" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pyrsistent-0.19.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:20460ac0ea439a3e79caa1dbd560344b64ed75e85d8703943e0b66c2a6150e4a"}, + {file = "pyrsistent-0.19.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c18264cb84b5e68e7085a43723f9e4c1fd1d935ab240ce02c0324a8e01ccb64"}, + {file = "pyrsistent-0.19.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b774f9288dda8d425adb6544e5903f1fb6c273ab3128a355c6b972b7df39dcf"}, + {file = "pyrsistent-0.19.3-cp310-cp310-win32.whl", hash = "sha256:5a474fb80f5e0d6c9394d8db0fc19e90fa540b82ee52dba7d246a7791712f74a"}, + {file = "pyrsistent-0.19.3-cp310-cp310-win_amd64.whl", hash = "sha256:49c32f216c17148695ca0e02a5c521e28a4ee6c5089f97e34fe24163113722da"}, + {file = "pyrsistent-0.19.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f0774bf48631f3a20471dd7c5989657b639fd2d285b861237ea9e82c36a415a9"}, + {file = "pyrsistent-0.19.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ab2204234c0ecd8b9368dbd6a53e83c3d4f3cab10ecaf6d0e772f456c442393"}, + {file = "pyrsistent-0.19.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e42296a09e83028b3476f7073fcb69ffebac0e66dbbfd1bd847d61f74db30f19"}, + {file = "pyrsistent-0.19.3-cp311-cp311-win32.whl", hash = "sha256:64220c429e42a7150f4bfd280f6f4bb2850f95956bde93c6fda1b70507af6ef3"}, + {file = "pyrsistent-0.19.3-cp311-cp311-win_amd64.whl", hash = "sha256:016ad1afadf318eb7911baa24b049909f7f3bb2c5b1ed7b6a8f21db21ea3faa8"}, + {file = "pyrsistent-0.19.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c4db1bd596fefd66b296a3d5d943c94f4fac5bcd13e99bffe2ba6a759d959a28"}, + {file = "pyrsistent-0.19.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aeda827381f5e5d65cced3024126529ddc4289d944f75e090572c77ceb19adbf"}, + {file = "pyrsistent-0.19.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:42ac0b2f44607eb92ae88609eda931a4f0dfa03038c44c772e07f43e738bcac9"}, + {file = "pyrsistent-0.19.3-cp37-cp37m-win32.whl", hash = "sha256:e8f2b814a3dc6225964fa03d8582c6e0b6650d68a232df41e3cc1b66a5d2f8d1"}, + {file = "pyrsistent-0.19.3-cp37-cp37m-win_amd64.whl", hash = "sha256:c9bb60a40a0ab9aba40a59f68214eed5a29c6274c83b2cc206a359c4a89fa41b"}, + {file = "pyrsistent-0.19.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a2471f3f8693101975b1ff85ffd19bb7ca7dd7c38f8a81701f67d6b4f97b87d8"}, + {file = "pyrsistent-0.19.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc5d149f31706762c1f8bda2e8c4f8fead6e80312e3692619a75301d3dbb819a"}, + {file = "pyrsistent-0.19.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3311cb4237a341aa52ab8448c27e3a9931e2ee09561ad150ba94e4cfd3fc888c"}, + {file = "pyrsistent-0.19.3-cp38-cp38-win32.whl", hash = "sha256:f0e7c4b2f77593871e918be000b96c8107da48444d57005b6a6bc61fb4331b2c"}, + {file = "pyrsistent-0.19.3-cp38-cp38-win_amd64.whl", hash = "sha256:c147257a92374fde8498491f53ffa8f4822cd70c0d85037e09028e478cababb7"}, + {file = "pyrsistent-0.19.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b735e538f74ec31378f5a1e3886a26d2ca6351106b4dfde376a26fc32a044edc"}, + {file = "pyrsistent-0.19.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99abb85579e2165bd8522f0c0138864da97847875ecbd45f3e7e2af569bfc6f2"}, + {file = "pyrsistent-0.19.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3a8cb235fa6d3fd7aae6a4f1429bbb1fec1577d978098da1252f0489937786f3"}, + {file = "pyrsistent-0.19.3-cp39-cp39-win32.whl", hash = "sha256:c74bed51f9b41c48366a286395c67f4e894374306b197e62810e0fdaf2364da2"}, + {file = "pyrsistent-0.19.3-cp39-cp39-win_amd64.whl", hash = "sha256:878433581fc23e906d947a6814336eee031a00e6defba224234169ae3d3d6a98"}, + {file = "pyrsistent-0.19.3-py3-none-any.whl", hash = "sha256:ccf0d6bd208f8111179f0c26fdf84ed7c3891982f2edaeae7422575f47e66b64"}, + {file = "pyrsistent-0.19.3.tar.gz", hash = "sha256:1a2994773706bbb4995c31a97bc94f1418314923bd1048c6d964837040376440"}, +] + +[[package]] +name = "python-dateutil" +version = "2.8.2" +description = "Extensions to the standard Python datetime module" +category = "main" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, + {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "python-json-logger" +version = "2.0.4" +description = "A python library adding a json log formatter" +category = "main" +optional = false +python-versions = ">=3.5" +files = [ + {file = "python-json-logger-2.0.4.tar.gz", hash = "sha256:764d762175f99fcc4630bd4853b09632acb60a6224acb27ce08cd70f0b1b81bd"}, + {file = "python_json_logger-2.0.4-py3-none-any.whl", hash = "sha256:3b03487b14eb9e4f77e4fc2a023358b5394b82fd89cecf5586259baed57d8c6f"}, +] + +[[package]] +name = "pytz" +version = "2022.7" +description = "World timezone definitions, modern and historical" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "pytz-2022.7-py2.py3-none-any.whl", hash = "sha256:93007def75ae22f7cd991c84e02d434876818661f8df9ad5df9e950ff4e52cfd"}, + {file = "pytz-2022.7.tar.gz", hash = "sha256:7ccfae7b4b2c067464a6733c6261673fdb8fd1be905460396b97a073e9fa683a"}, +] + +[[package]] +name = "pyvirtualdisplay" +version = "3.0" +description = "python wrapper for Xvfb, Xephyr and Xvnc" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "PyVirtualDisplay-3.0-py3-none-any.whl", hash = "sha256:40d4b8dfe4b8de8552e28eb367647f311f88a130bf837fe910e7f180d5477f0e"}, + {file = "PyVirtualDisplay-3.0.tar.gz", hash = "sha256:09755bc3ceb6eb725fb07eca5425f43f2358d3bf08e00d2a9b792a1aedd16159"}, +] + +[[package]] +name = "pywin32" +version = "305" +description = "Python for Window Extensions" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "pywin32-305-cp310-cp310-win32.whl", hash = "sha256:421f6cd86e84bbb696d54563c48014b12a23ef95a14e0bdba526be756d89f116"}, + {file = "pywin32-305-cp310-cp310-win_amd64.whl", hash = "sha256:73e819c6bed89f44ff1d690498c0a811948f73777e5f97c494c152b850fad478"}, + {file = "pywin32-305-cp310-cp310-win_arm64.whl", hash = "sha256:742eb905ce2187133a29365b428e6c3b9001d79accdc30aa8969afba1d8470f4"}, + {file = "pywin32-305-cp311-cp311-win32.whl", hash = "sha256:19ca459cd2e66c0e2cc9a09d589f71d827f26d47fe4a9d09175f6aa0256b51c2"}, + {file = "pywin32-305-cp311-cp311-win_amd64.whl", hash = "sha256:326f42ab4cfff56e77e3e595aeaf6c216712bbdd91e464d167c6434b28d65990"}, + {file = "pywin32-305-cp311-cp311-win_arm64.whl", hash = "sha256:4ecd404b2c6eceaca52f8b2e3e91b2187850a1ad3f8b746d0796a98b4cea04db"}, + {file = "pywin32-305-cp36-cp36m-win32.whl", hash = "sha256:48d8b1659284f3c17b68587af047d110d8c44837736b8932c034091683e05863"}, + {file = "pywin32-305-cp36-cp36m-win_amd64.whl", hash = "sha256:13362cc5aa93c2beaf489c9c9017c793722aeb56d3e5166dadd5ef82da021fe1"}, + {file = "pywin32-305-cp37-cp37m-win32.whl", hash = "sha256:a55db448124d1c1484df22fa8bbcbc45c64da5e6eae74ab095b9ea62e6d00496"}, + {file = "pywin32-305-cp37-cp37m-win_amd64.whl", hash = "sha256:109f98980bfb27e78f4df8a51a8198e10b0f347257d1e265bb1a32993d0c973d"}, + {file = "pywin32-305-cp38-cp38-win32.whl", hash = "sha256:9dd98384da775afa009bc04863426cb30596fd78c6f8e4e2e5bbf4edf8029504"}, + {file = "pywin32-305-cp38-cp38-win_amd64.whl", hash = "sha256:56d7a9c6e1a6835f521788f53b5af7912090674bb84ef5611663ee1595860fc7"}, + {file = "pywin32-305-cp39-cp39-win32.whl", hash = "sha256:9d968c677ac4d5cbdaa62fd3014ab241718e619d8e36ef8e11fb930515a1e918"}, + {file = "pywin32-305-cp39-cp39-win_amd64.whl", hash = "sha256:50768c6b7c3f0b38b7fb14dd4104da93ebced5f1a50dc0e834594bff6fbe1271"}, +] + +[[package]] +name = "pywin32-ctypes" +version = "0.2.0" +description = "" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "pywin32-ctypes-0.2.0.tar.gz", hash = "sha256:24ffc3b341d457d48e8922352130cf2644024a4ff09762a2261fd34c36ee5942"}, + {file = "pywin32_ctypes-0.2.0-py2.py3-none-any.whl", hash = "sha256:9dc2d991b3479cc2df15930958b674a48a227d5361d413827a4cfd0b5876fc98"}, +] + +[[package]] +name = "pywinpty" +version = "2.0.10" +description = "Pseudo terminal support for Windows from Python." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pywinpty-2.0.10-cp310-none-win_amd64.whl", hash = "sha256:4c7d06ad10f6e92bc850a467f26d98f4f30e73d2fe5926536308c6ae0566bc16"}, + {file = "pywinpty-2.0.10-cp311-none-win_amd64.whl", hash = "sha256:7ffbd66310b83e42028fc9df7746118978d94fba8c1ebf15a7c1275fdd80b28a"}, + {file = "pywinpty-2.0.10-cp37-none-win_amd64.whl", hash = "sha256:38cb924f2778b5751ef91a75febd114776b3af0ae411bc667be45dd84fc881d3"}, + {file = "pywinpty-2.0.10-cp38-none-win_amd64.whl", hash = "sha256:902d79444b29ad1833b8d5c3c9aabdfd428f4f068504430df18074007c8c0de8"}, + {file = "pywinpty-2.0.10-cp39-none-win_amd64.whl", hash = "sha256:3c46aef80dd50979aff93de199e4a00a8ee033ba7a03cadf0a91fed45f0c39d7"}, + {file = "pywinpty-2.0.10.tar.gz", hash = "sha256:cdbb5694cf8c7242c2ecfaca35c545d31fa5d5814c3d67a4e628f803f680ebea"}, +] + +[[package]] +name = "pyyaml" +version = "6.0" +description = "YAML parser and emitter for Python" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, + {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"}, + {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"}, + {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"}, + {file = "PyYAML-6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358"}, + {file = "PyYAML-6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1"}, + {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d"}, + {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f"}, + {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782"}, + {file = "PyYAML-6.0-cp311-cp311-win32.whl", hash = "sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7"}, + {file = "PyYAML-6.0-cp311-cp311-win_amd64.whl", hash = "sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf"}, + {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"}, + {file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"}, + {file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"}, + {file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"}, + {file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"}, + {file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"}, + {file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"}, + {file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"}, + {file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"}, + {file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"}, + {file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"}, + {file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"}, + {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, + {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, +] + +[[package]] +name = "pyzmq" +version = "25.0.0" +description = "Python bindings for 0MQ" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pyzmq-25.0.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:2d05d904f03ddf1e0d83d97341354dfe52244a619b5a1440a5f47a5b3451e84e"}, + {file = "pyzmq-25.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a154ef810d44f9d28868be04641f837374a64e7449df98d9208e76c260c7ef1"}, + {file = "pyzmq-25.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:487305c2a011fdcf3db1f24e8814bb76d23bc4d2f46e145bc80316a59a9aa07d"}, + {file = "pyzmq-25.0.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e7b87638ee30ab13230e37ce5331b3e730b1e0dda30120b9eeec3540ed292c8"}, + {file = "pyzmq-25.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75243e422e85a62f0ab7953dc315452a56b2c6a7e7d1a3c3109ac3cc57ed6b47"}, + {file = "pyzmq-25.0.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:31e523d067ce44a04e876bed3ff9ea1ff8d1b6636d16e5fcace9d22f8c564369"}, + {file = "pyzmq-25.0.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:8539216173135e9e89f6b1cc392e74e6b935b91e8c76106cf50e7a02ab02efe5"}, + {file = "pyzmq-25.0.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:2754fa68da08a854f4816e05160137fa938a2347276471103d31e04bcee5365c"}, + {file = "pyzmq-25.0.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4a1bc30f0c18444d51e9b0d0dd39e3a4e7c53ee74190bebef238cd58de577ea9"}, + {file = "pyzmq-25.0.0-cp310-cp310-win32.whl", hash = "sha256:01d53958c787cfea34091fcb8ef36003dbb7913b8e9f8f62a0715234ebc98b70"}, + {file = "pyzmq-25.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:58fc3ad5e1cfd2e6d24741fbb1e216b388115d31b0ca6670f894187f280b6ba6"}, + {file = "pyzmq-25.0.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:e4bba04ea779a3d7ef25a821bb63fd0939142c88e7813e5bd9c6265a20c523a2"}, + {file = "pyzmq-25.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:af1fbfb7ad6ac0009ccee33c90a1d303431c7fb594335eb97760988727a37577"}, + {file = "pyzmq-25.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85456f0d8f3268eecd63dede3b99d5bd8d3b306310c37d4c15141111d22baeaf"}, + {file = "pyzmq-25.0.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0645b5a2d2a06fd8eb738018490c514907f7488bf9359c6ee9d92f62e844b76f"}, + {file = "pyzmq-25.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f72ea279b2941a5203e935a4588b9ba8a48aeb9a926d9dfa1986278bd362cb8"}, + {file = "pyzmq-25.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:4e295f7928a31ae0f657e848c5045ba6d693fe8921205f408ca3804b1b236968"}, + {file = "pyzmq-25.0.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ac97e7d647d5519bcef48dd8d3d331f72975afa5c4496c95f6e854686f45e2d9"}, + {file = "pyzmq-25.0.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:656281d496aaf9ca4fd4cea84e6d893e3361057c4707bd38618f7e811759103c"}, + {file = "pyzmq-25.0.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1f6116991568aac48b94d6d8aaed6157d407942ea385335a6ed313692777fb9d"}, + {file = "pyzmq-25.0.0-cp311-cp311-win32.whl", hash = "sha256:0282bba9aee6e0346aa27d6c69b5f7df72b5a964c91958fc9e0c62dcae5fdcdc"}, + {file = "pyzmq-25.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:526f884a27e8bba62fe1f4e07c62be2cfe492b6d432a8fdc4210397f8cf15331"}, + {file = "pyzmq-25.0.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:ccb3e1a863222afdbda42b7ca8ac8569959593d7abd44f5a709177d6fa27d266"}, + {file = "pyzmq-25.0.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4046d03100aca266e70d54a35694cb35d6654cfbef633e848b3c4a8d64b9d187"}, + {file = "pyzmq-25.0.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3100dddcada66ec5940ed6391ebf9d003cc3ede3d320748b2737553019f58230"}, + {file = "pyzmq-25.0.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:7877264aa851c19404b1bb9dbe6eed21ea0c13698be1eda3784aab3036d1c861"}, + {file = "pyzmq-25.0.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:5049e75cc99db65754a3da5f079230fb8889230cf09462ec972d884d1704a3ed"}, + {file = "pyzmq-25.0.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:81f99fb1224d36eb91557afec8cdc2264e856f3464500b55749020ce4c848ef2"}, + {file = "pyzmq-25.0.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:a1cd4a95f176cdc0ee0a82d49d5830f13ae6015d89decbf834c273bc33eeb3d3"}, + {file = "pyzmq-25.0.0-cp36-cp36m-win32.whl", hash = "sha256:926236ca003aec70574754f39703528947211a406f5c6c8b3e50eca04a9e87fc"}, + {file = "pyzmq-25.0.0-cp36-cp36m-win_amd64.whl", hash = "sha256:94f0a7289d0f5c80807c37ebb404205e7deb737e8763eb176f4770839ee2a287"}, + {file = "pyzmq-25.0.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:f3f96d452e9580cb961ece2e5a788e64abaecb1232a80e61deffb28e105ff84a"}, + {file = "pyzmq-25.0.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:930e6ad4f2eaac31a3d0c2130619d25db754b267487ebc186c6ad18af2a74018"}, + {file = "pyzmq-25.0.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e1081d7030a1229c8ff90120346fb7599b54f552e98fcea5170544e7c6725aab"}, + {file = "pyzmq-25.0.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:531866c491aee5a1e967c286cfa470dffac1e2a203b1afda52d62b58782651e9"}, + {file = "pyzmq-25.0.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:fc7c1421c5b1c916acf3128bf3cc7ea7f5018b58c69a6866d70c14190e600ce9"}, + {file = "pyzmq-25.0.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:9a2d5e419bd39a1edb6cdd326d831f0120ddb9b1ff397e7d73541bf393294973"}, + {file = "pyzmq-25.0.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:183e18742be3621acf8908903f689ec520aee3f08449bfd29f583010ca33022b"}, + {file = "pyzmq-25.0.0-cp37-cp37m-win32.whl", hash = "sha256:02f5cb60a7da1edd5591a15efa654ffe2303297a41e1b40c3c8942f8f11fc17c"}, + {file = "pyzmq-25.0.0-cp37-cp37m-win_amd64.whl", hash = "sha256:cac602e02341eaaf4edfd3e29bd3fdef672e61d4e6dfe5c1d065172aee00acee"}, + {file = "pyzmq-25.0.0-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:e14df47c1265356715d3d66e90282a645ebc077b70b3806cf47efcb7d1d630cb"}, + {file = "pyzmq-25.0.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:293a7c2128690f496057f1f1eb6074f8746058d13588389981089ec45d8fdc77"}, + {file = "pyzmq-25.0.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:731b208bc9412deeb553c9519dca47136b5a01ca66667cafd8733211941b17e4"}, + {file = "pyzmq-25.0.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b055a1cddf8035966ad13aa51edae5dc8f1bba0b5d5e06f7a843d8b83dc9b66b"}, + {file = "pyzmq-25.0.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:17e1cb97d573ea84d7cd97188b42ca6f611ab3ee600f6a75041294ede58e3d20"}, + {file = "pyzmq-25.0.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:60ecbfe7669d3808ffa8a7dd1487d6eb8a4015b07235e3b723d4b2a2d4de7203"}, + {file = "pyzmq-25.0.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4c25c95416133942280faaf068d0fddfd642b927fb28aaf4ab201a738e597c1e"}, + {file = "pyzmq-25.0.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:be05504af0619d1cffa500af1e0ede69fb683f301003851f5993b5247cc2c576"}, + {file = "pyzmq-25.0.0-cp38-cp38-win32.whl", hash = "sha256:6bf3842af37af43fa953e96074ebbb5315f6a297198f805d019d788a1021dbc8"}, + {file = "pyzmq-25.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:b90bb8dfbbd138558f1f284fecfe328f7653616ff9a972433a00711d9475d1a9"}, + {file = "pyzmq-25.0.0-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:62b9e80890c0d2408eb42d5d7e1fc62a5ce71be3288684788f74cf3e59ffd6e2"}, + {file = "pyzmq-25.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:484c2c4ee02c1edc07039f42130bd16e804b1fe81c4f428e0042e03967f40c20"}, + {file = "pyzmq-25.0.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9ca6db34b26c4d3e9b0728841ec9aa39484eee272caa97972ec8c8e231b20c7e"}, + {file = "pyzmq-25.0.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:610d2d112acd4e5501fac31010064a6c6efd716ceb968e443cae0059eb7b86de"}, + {file = "pyzmq-25.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3594c0ff604e685d7e907860b61d0e10e46c74a9ffca168f6e9e50ea934ee440"}, + {file = "pyzmq-25.0.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c21a5f4e54a807df5afdef52b6d24ec1580153a6bcf0607f70a6e1d9fa74c5c3"}, + {file = "pyzmq-25.0.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:4725412e27612f0d7d7c2f794d89807ad0227c2fc01dd6146b39ada49c748ef9"}, + {file = "pyzmq-25.0.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4d3d604fe0a67afd1aff906e54da557a5203368a99dcc50a70eef374f1d2abef"}, + {file = "pyzmq-25.0.0-cp39-cp39-win32.whl", hash = "sha256:3670e8c5644768f214a3b598fe46378a4a6f096d5fb82a67dfd3440028460565"}, + {file = "pyzmq-25.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:e99629a976809fe102ef73e856cf4b2660acd82a412a51e80ba2215e523dfd0a"}, + {file = "pyzmq-25.0.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:66509c48f7446b640eeae24b60c9c1461799a27b1b0754e438582e36b5af3315"}, + {file = "pyzmq-25.0.0-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a9c464cc508177c09a5a6122b67f978f20e2954a21362bf095a0da4647e3e908"}, + {file = "pyzmq-25.0.0-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:28bcb2e66224a7ac2843eb632e4109d6b161479e7a2baf24e37210461485b4f1"}, + {file = "pyzmq-25.0.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0e7ef9ac807db50b4eb6f534c5dcc22f998f5dae920cc28873d2c1d080a4fc9"}, + {file = "pyzmq-25.0.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:5050f5c50b58a6e38ccaf9263a356f74ef1040f5ca4030225d1cb1a858c5b7b6"}, + {file = "pyzmq-25.0.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:2a73af6504e0d2805e926abf136ebf536735a13c22f709be7113c2ec65b4bec3"}, + {file = "pyzmq-25.0.0-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0e8d00228db627ddd1b418c7afd81820b38575f237128c9650365f2dd6ac3443"}, + {file = "pyzmq-25.0.0-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5605621f2181f20b71f13f698944deb26a0a71af4aaf435b34dd90146092d530"}, + {file = "pyzmq-25.0.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6136bfb0e5a9cf8c60c6ac763eb21f82940a77e6758ea53516c8c7074f4ff948"}, + {file = "pyzmq-25.0.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:0a90b2480a26aef7c13cff18703ba8d68e181facb40f78873df79e6d42c1facc"}, + {file = "pyzmq-25.0.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:00c94fd4c9dd3c95aace0c629a7fa713627a5c80c1819326b642adf6c4b8e2a2"}, + {file = "pyzmq-25.0.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20638121b0bdc80777ce0ec8c1f14f1ffec0697a1f88f0b564fa4a23078791c4"}, + {file = "pyzmq-25.0.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b6f75b4b8574f3a8a0d6b4b52606fc75b82cb4391471be48ab0b8677c82f9ed4"}, + {file = "pyzmq-25.0.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4cbb885f347eba7ab7681c450dee5b14aed9f153eec224ec0c3f299273d9241f"}, + {file = "pyzmq-25.0.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c48f257da280b3be6c94e05bd575eddb1373419dbb1a72c3ce64e88f29d1cd6d"}, + {file = "pyzmq-25.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:866eabf7c1315ef2e93e34230db7cbf672e0d7c626b37c11f7e870c8612c3dcc"}, + {file = "pyzmq-25.0.0.tar.gz", hash = "sha256:f330a1a2c7f89fd4b0aa4dcb7bf50243bf1c8da9a2f1efc31daf57a2046b31f2"}, +] + +[package.dependencies] +cffi = {version = "*", markers = "implementation_name == \"pypy\""} + +[[package]] +name = "qtconsole" +version = "5.4.0" +description = "Jupyter Qt console" +category = "main" +optional = false +python-versions = ">= 3.7" +files = [ + {file = "qtconsole-5.4.0-py3-none-any.whl", hash = "sha256:be13560c19bdb3b54ed9741a915aa701a68d424519e8341ac479a91209e694b2"}, + {file = "qtconsole-5.4.0.tar.gz", hash = "sha256:57748ea2fd26320a0b77adba20131cfbb13818c7c96d83fafcb110ff55f58b35"}, +] + +[package.dependencies] +ipykernel = ">=4.1" +ipython-genutils = "*" +jupyter-client = ">=4.1" +jupyter-core = "*" +pygments = "*" +pyzmq = ">=17.1" +qtpy = ">=2.0.1" +traitlets = "<5.2.1 || >5.2.1,<5.2.2 || >5.2.2" + +[package.extras] +doc = ["Sphinx (>=1.3)"] +test = ["flaky", "pytest", "pytest-qt"] + +[[package]] +name = "qtpy" +version = "2.3.0" +description = "Provides an abstraction layer on top of the various Qt bindings (PyQt5/6 and PySide2/6)." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "QtPy-2.3.0-py3-none-any.whl", hash = "sha256:8d6d544fc20facd27360ea189592e6135c614785f0dec0b4f083289de6beb408"}, + {file = "QtPy-2.3.0.tar.gz", hash = "sha256:0603c9c83ccc035a4717a12908bf6bc6cb22509827ea2ec0e94c2da7c9ed57c5"}, +] + +[package.dependencies] +packaging = "*" + +[package.extras] +test = ["pytest (>=6,!=7.0.0,!=7.0.1)", "pytest-cov (>=3.0.0)", "pytest-qt"] + +[[package]] +name = "requests" +version = "2.28.2" +description = "Python HTTP for Humans." +category = "main" +optional = false +python-versions = ">=3.7, <4" +files = [ + {file = "requests-2.28.2-py3-none-any.whl", hash = "sha256:64299f4909223da747622c030b781c0d7811e359c37124b4bd368fb8c6518baa"}, + {file = "requests-2.28.2.tar.gz", hash = "sha256:98b1b2782e3c6c4904938b84c0eb932721069dfdb9134313beff7c83c2df24bf"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<1.27" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "requests-oauthlib" +version = "1.3.1" +description = "OAuthlib authentication support for Requests." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "requests-oauthlib-1.3.1.tar.gz", hash = "sha256:75beac4a47881eeb94d5ea5d6ad31ef88856affe2332b9aafb52c6452ccf0d7a"}, + {file = "requests_oauthlib-1.3.1-py2.py3-none-any.whl", hash = "sha256:2577c501a2fb8d05a304c09d090d6e47c306fef15809d102b327cf8364bddab5"}, +] + +[package.dependencies] +oauthlib = ">=3.0.0" +requests = ">=2.0.0" + +[package.extras] +rsa = ["oauthlib[signedtoken] (>=3.0.0)"] + +[[package]] +name = "rfc3339-validator" +version = "0.1.4" +description = "A pure python RFC3339 validator" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "rfc3339_validator-0.1.4-py2.py3-none-any.whl", hash = "sha256:24f6ec1eda14ef823da9e36ec7113124b39c04d50a4d3d3a3c2859577e7791fa"}, + {file = "rfc3339_validator-0.1.4.tar.gz", hash = "sha256:138a2abdf93304ad60530167e51d2dfb9549521a836871b88d7f4695d0022f6b"}, +] + +[package.dependencies] +six = "*" + +[[package]] +name = "rfc3986-validator" +version = "0.1.1" +description = "Pure python rfc3986 validator" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "rfc3986_validator-0.1.1-py2.py3-none-any.whl", hash = "sha256:2f235c432ef459970b4306369336b9d5dbdda31b510ca1e327636e01f528bfa9"}, + {file = "rfc3986_validator-0.1.1.tar.gz", hash = "sha256:3d44bde7921b3b9ec3ae4e3adca370438eccebc676456449b145d533b240d055"}, +] + +[[package]] +name = "rich" +version = "13.0.1" +description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" +category = "main" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "rich-13.0.1-py3-none-any.whl", hash = "sha256:41fe1d05f433b0f4724cda8345219213d2bfa472ef56b2f64f415b5b94d51b04"}, + {file = "rich-13.0.1.tar.gz", hash = "sha256:25f83363f636995627a99f6e4abc52ed0970ebbd544960cc63cbb43aaac3d6f0"}, +] + +[package.dependencies] +commonmark = ">=0.9.0,<0.10.0" +pygments = ">=2.6.0,<3.0.0" + +[package.extras] +jupyter = ["ipywidgets (>=7.5.1,<8.0.0)"] + +[[package]] +name = "rsa" +version = "4.9" +description = "Pure-Python RSA implementation" +category = "main" +optional = false +python-versions = ">=3.6,<4" +files = [ + {file = "rsa-4.9-py3-none-any.whl", hash = "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7"}, + {file = "rsa-4.9.tar.gz", hash = "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21"}, +] + +[package.dependencies] +pyasn1 = ">=0.1.3" + +[[package]] +name = "ruamel-yaml" +version = "0.17.21" +description = "ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order" +category = "main" +optional = false +python-versions = ">=3" +files = [ + {file = "ruamel.yaml-0.17.21-py3-none-any.whl", hash = "sha256:742b35d3d665023981bd6d16b3d24248ce5df75fdb4e2924e93a05c1f8b61ca7"}, + {file = "ruamel.yaml-0.17.21.tar.gz", hash = "sha256:8b7ce697a2f212752a35c1ac414471dc16c424c9573be4926b56ff3f5d23b7af"}, +] + +[package.dependencies] +"ruamel.yaml.clib" = {version = ">=0.2.6", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.11\""} + +[package.extras] +docs = ["ryd"] +jinja2 = ["ruamel.yaml.jinja2 (>=0.2)"] + +[[package]] +name = "ruamel-yaml-clib" +version = "0.2.7" +description = "C version of reader, parser and emitter for ruamel.yaml derived from libyaml" +category = "main" +optional = false +python-versions = ">=3.5" +files = [ + {file = "ruamel.yaml.clib-0.2.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d5859983f26d8cd7bb5c287ef452e8aacc86501487634573d260968f753e1d71"}, + {file = "ruamel.yaml.clib-0.2.7-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:debc87a9516b237d0466a711b18b6ebeb17ba9f391eb7f91c649c5c4ec5006c7"}, + {file = "ruamel.yaml.clib-0.2.7-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:df5828871e6648db72d1c19b4bd24819b80a755c4541d3409f0f7acd0f335c80"}, + {file = "ruamel.yaml.clib-0.2.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:efa08d63ef03d079dcae1dfe334f6c8847ba8b645d08df286358b1f5293d24ab"}, + {file = "ruamel.yaml.clib-0.2.7-cp310-cp310-win32.whl", hash = "sha256:763d65baa3b952479c4e972669f679fe490eee058d5aa85da483ebae2009d231"}, + {file = "ruamel.yaml.clib-0.2.7-cp310-cp310-win_amd64.whl", hash = "sha256:d000f258cf42fec2b1bbf2863c61d7b8918d31ffee905da62dede869254d3b8a"}, + {file = "ruamel.yaml.clib-0.2.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:045e0626baf1c52e5527bd5db361bc83180faaba2ff586e763d3d5982a876a9e"}, + {file = "ruamel.yaml.clib-0.2.7-cp311-cp311-macosx_12_6_arm64.whl", hash = "sha256:721bc4ba4525f53f6a611ec0967bdcee61b31df5a56801281027a3a6d1c2daf5"}, + {file = "ruamel.yaml.clib-0.2.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:41d0f1fa4c6830176eef5b276af04c89320ea616655d01327d5ce65e50575c94"}, + {file = "ruamel.yaml.clib-0.2.7-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:4b3a93bb9bc662fc1f99c5c3ea8e623d8b23ad22f861eb6fce9377ac07ad6072"}, + {file = "ruamel.yaml.clib-0.2.7-cp36-cp36m-macosx_12_0_arm64.whl", hash = "sha256:a234a20ae07e8469da311e182e70ef6b199d0fbeb6c6cc2901204dd87fb867e8"}, + {file = "ruamel.yaml.clib-0.2.7-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:15910ef4f3e537eea7fe45f8a5d19997479940d9196f357152a09031c5be59f3"}, + {file = "ruamel.yaml.clib-0.2.7-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:370445fd795706fd291ab00c9df38a0caed0f17a6fb46b0f607668ecb16ce763"}, + {file = "ruamel.yaml.clib-0.2.7-cp36-cp36m-win32.whl", hash = "sha256:ecdf1a604009bd35c674b9225a8fa609e0282d9b896c03dd441a91e5f53b534e"}, + {file = "ruamel.yaml.clib-0.2.7-cp36-cp36m-win_amd64.whl", hash = "sha256:f34019dced51047d6f70cb9383b2ae2853b7fc4dce65129a5acd49f4f9256646"}, + {file = "ruamel.yaml.clib-0.2.7-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:2aa261c29a5545adfef9296b7e33941f46aa5bbd21164228e833412af4c9c75f"}, + {file = "ruamel.yaml.clib-0.2.7-cp37-cp37m-macosx_12_0_arm64.whl", hash = "sha256:f01da5790e95815eb5a8a138508c01c758e5f5bc0ce4286c4f7028b8dd7ac3d0"}, + {file = "ruamel.yaml.clib-0.2.7-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:40d030e2329ce5286d6b231b8726959ebbe0404c92f0a578c0e2482182e38282"}, + {file = "ruamel.yaml.clib-0.2.7-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:c3ca1fbba4ae962521e5eb66d72998b51f0f4d0f608d3c0347a48e1af262efa7"}, + {file = "ruamel.yaml.clib-0.2.7-cp37-cp37m-win32.whl", hash = "sha256:7bdb4c06b063f6fd55e472e201317a3bb6cdeeee5d5a38512ea5c01e1acbdd93"}, + {file = "ruamel.yaml.clib-0.2.7-cp37-cp37m-win_amd64.whl", hash = "sha256:be2a7ad8fd8f7442b24323d24ba0b56c51219513cfa45b9ada3b87b76c374d4b"}, + {file = "ruamel.yaml.clib-0.2.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:91a789b4aa0097b78c93e3dc4b40040ba55bef518f84a40d4442f713b4094acb"}, + {file = "ruamel.yaml.clib-0.2.7-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:99e77daab5d13a48a4054803d052ff40780278240a902b880dd37a51ba01a307"}, + {file = "ruamel.yaml.clib-0.2.7-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:3243f48ecd450eddadc2d11b5feb08aca941b5cd98c9b1db14b2fd128be8c697"}, + {file = "ruamel.yaml.clib-0.2.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:8831a2cedcd0f0927f788c5bdf6567d9dc9cc235646a434986a852af1cb54b4b"}, + {file = "ruamel.yaml.clib-0.2.7-cp38-cp38-win32.whl", hash = "sha256:3110a99e0f94a4a3470ff67fc20d3f96c25b13d24c6980ff841e82bafe827cac"}, + {file = "ruamel.yaml.clib-0.2.7-cp38-cp38-win_amd64.whl", hash = "sha256:92460ce908546ab69770b2e576e4f99fbb4ce6ab4b245345a3869a0a0410488f"}, + {file = "ruamel.yaml.clib-0.2.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5bc0667c1eb8f83a3752b71b9c4ba55ef7c7058ae57022dd9b29065186a113d9"}, + {file = "ruamel.yaml.clib-0.2.7-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:4a4d8d417868d68b979076a9be6a38c676eca060785abaa6709c7b31593c35d1"}, + {file = "ruamel.yaml.clib-0.2.7-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:bf9a6bc4a0221538b1a7de3ed7bca4c93c02346853f44e1cd764be0023cd3640"}, + {file = "ruamel.yaml.clib-0.2.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:a7b301ff08055d73223058b5c46c55638917f04d21577c95e00e0c4d79201a6b"}, + {file = "ruamel.yaml.clib-0.2.7-cp39-cp39-win32.whl", hash = "sha256:d5e51e2901ec2366b79f16c2299a03e74ba4531ddcfacc1416639c557aef0ad8"}, + {file = "ruamel.yaml.clib-0.2.7-cp39-cp39-win_amd64.whl", hash = "sha256:184faeaec61dbaa3cace407cffc5819f7b977e75360e8d5ca19461cd851a5fc5"}, + {file = "ruamel.yaml.clib-0.2.7.tar.gz", hash = "sha256:1f08fd5a2bea9c4180db71678e850b995d2a5f4537be0e94557668cf0f5f9497"}, +] + +[[package]] +name = "scipy" +version = "1.10.0" +description = "Fundamental algorithms for scientific computing in Python" +category = "main" +optional = false +python-versions = "<3.12,>=3.8" +files = [ + {file = "scipy-1.10.0-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:b901b423c91281a974f6cd1c36f5c6c523e665b5a6d5e80fcb2334e14670eefd"}, + {file = "scipy-1.10.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:16ba05d3d1b9f2141004f3f36888e05894a525960b07f4c2bfc0456b955a00be"}, + {file = "scipy-1.10.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:151f066fe7d6653c3ffefd489497b8fa66d7316e3e0d0c0f7ff6acca1b802809"}, + {file = "scipy-1.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2f9ea0a37aca111a407cb98aa4e8dfde6e5d9333bae06dfa5d938d14c80bb5c3"}, + {file = "scipy-1.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:27e548276b5a88b51212b61f6dda49a24acf5d770dff940bd372b3f7ced8c6c2"}, + {file = "scipy-1.10.0-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:42ab8b9e7dc1ebe248e55f54eea5307b6ab15011a7883367af48dd781d1312e4"}, + {file = "scipy-1.10.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:e096b062d2efdea57f972d232358cb068413dc54eec4f24158bcbb5cb8bddfd8"}, + {file = "scipy-1.10.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4df25a28bd22c990b22129d3c637fd5c3be4b7c94f975dca909d8bab3309b694"}, + {file = "scipy-1.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ad449db4e0820e4b42baccefc98ec772ad7818dcbc9e28b85aa05a536b0f1a2"}, + {file = "scipy-1.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:6faf86ef7717891195ae0537e48da7524d30bc3b828b30c9b115d04ea42f076f"}, + {file = "scipy-1.10.0-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:4bd0e3278126bc882d10414436e58fa3f1eca0aa88b534fcbf80ed47e854f46c"}, + {file = "scipy-1.10.0-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:38bfbd18dcc69eeb589811e77fae552fa923067fdfbb2e171c9eac749885f210"}, + {file = "scipy-1.10.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ab2a58064836632e2cec31ca197d3695c86b066bc4818052b3f5381bfd2a728"}, + {file = "scipy-1.10.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5cd7a30970c29d9768a7164f564d1fbf2842bfc77b7d114a99bc32703ce0bf48"}, + {file = "scipy-1.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:9b878c671655864af59c108c20e4da1e796154bd78c0ed6bb02bc41c84625686"}, + {file = "scipy-1.10.0-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:3afcbddb4488ac950ce1147e7580178b333a29cd43524c689b2e3543a080a2c8"}, + {file = "scipy-1.10.0-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:6e4497e5142f325a5423ff5fda2fff5b5d953da028637ff7c704378c8c284ea7"}, + {file = "scipy-1.10.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:441cab2166607c82e6d7a8683779cb89ba0f475b983c7e4ab88f3668e268c143"}, + {file = "scipy-1.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0490dc499fe23e4be35b8b6dd1e60a4a34f0c4adb30ac671e6332446b3cbbb5a"}, + {file = "scipy-1.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:954ff69d2d1bf666b794c1d7216e0a746c9d9289096a64ab3355a17c7c59db54"}, + {file = "scipy-1.10.0.tar.gz", hash = "sha256:c8b3cbc636a87a89b770c6afc999baa6bcbb01691b5ccbbc1b1791c7c0a07540"}, +] + +[package.dependencies] +numpy = ">=1.19.5,<1.27.0" + +[package.extras] +dev = ["click", "doit (>=0.36.0)", "flake8", "mypy", "pycodestyle", "pydevtool", "rich-click", "typing_extensions"] +doc = ["matplotlib (>2)", "numpydoc", "pydata-sphinx-theme (==0.9.0)", "sphinx (!=4.1.0)", "sphinx-design (>=0.2.0)"] +test = ["asv", "gmpy2", "mpmath", "pooch", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "scikit-umfpack", "threadpoolctl"] + +[[package]] +name = "secretstorage" +version = "3.3.3" +description = "Python bindings to FreeDesktop.org Secret Service API" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "SecretStorage-3.3.3-py3-none-any.whl", hash = "sha256:f356e6628222568e3af06f2eba8df495efa13b3b63081dafd4f7d9a7b7bc9f99"}, + {file = "SecretStorage-3.3.3.tar.gz", hash = "sha256:2403533ef369eca6d2ba81718576c5e0f564d5cca1b58f73a8b23e7d4eeebd77"}, +] + +[package.dependencies] +cryptography = ">=2.0" +jeepney = ">=0.6" + +[[package]] +name = "send2trash" +version = "1.8.0" +description = "Send file to trash natively under Mac OS X, Windows and Linux." +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "Send2Trash-1.8.0-py3-none-any.whl", hash = "sha256:f20eaadfdb517eaca5ce077640cb261c7d2698385a6a0f072a4a5447fd49fa08"}, + {file = "Send2Trash-1.8.0.tar.gz", hash = "sha256:d2c24762fd3759860a0aff155e45871447ea58d2be6bdd39b5c8f966a0c99c2d"}, +] + +[package.extras] +nativelib = ["pyobjc-framework-Cocoa", "pywin32"] +objc = ["pyobjc-framework-Cocoa"] +win32 = ["pywin32"] + +[[package]] +name = "sentry-sdk" +version = "1.13.0" +description = "Python client for Sentry (https://sentry.io)" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "sentry-sdk-1.13.0.tar.gz", hash = "sha256:72da0766c3069a3941eadbdfa0996f83f5a33e55902a19ba399557cfee1dddcc"}, + {file = "sentry_sdk-1.13.0-py2.py3-none-any.whl", hash = "sha256:b7ff6318183e551145b5c4766eb65b59ad5b63ff234dffddc5fb50340cad6729"}, +] + +[package.dependencies] +certifi = "*" +urllib3 = {version = ">=1.26.11", markers = "python_version >= \"3.6\""} + +[package.extras] +aiohttp = ["aiohttp (>=3.5)"] +beam = ["apache-beam (>=2.12)"] +bottle = ["bottle (>=0.12.13)"] +celery = ["celery (>=3)"] +chalice = ["chalice (>=1.16.0)"] +django = ["django (>=1.8)"] +falcon = ["falcon (>=1.4)"] +fastapi = ["fastapi (>=0.79.0)"] +flask = ["blinker (>=1.1)", "flask (>=0.11)"] +httpx = ["httpx (>=0.16.0)"] +opentelemetry = ["opentelemetry-distro (>=0.350b0)"] +pure-eval = ["asttokens", "executing", "pure-eval"] +pymongo = ["pymongo (>=3.1)"] +pyspark = ["pyspark (>=2.4.4)"] +quart = ["blinker (>=1.1)", "quart (>=0.16.1)"] +rq = ["rq (>=0.6)"] +sanic = ["sanic (>=0.8)"] +sqlalchemy = ["sqlalchemy (>=1.2)"] +starlette = ["starlette (>=0.19.1)"] +starlite = ["starlite (>=1.48)"] +tornado = ["tornado (>=5)"] + +[[package]] +name = "setproctitle" +version = "1.3.2" +description = "A Python module to customize the process title" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "setproctitle-1.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:288943dec88e178bb2fd868adf491197cc0fc8b6810416b1c6775e686bab87fe"}, + {file = "setproctitle-1.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:630f6fe5e24a619ccf970c78e084319ee8be5be253ecc9b5b216b0f474f5ef18"}, + {file = "setproctitle-1.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c877691b90026670e5a70adfbcc735460a9f4c274d35ec5e8a43ce3f8443005"}, + {file = "setproctitle-1.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7a55fe05f15c10e8c705038777656fe45e3bd676d49ad9ac8370b75c66dd7cd7"}, + {file = "setproctitle-1.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ab45146c71ca6592c9cc8b354a2cc9cc4843c33efcbe1d245d7d37ce9696552d"}, + {file = "setproctitle-1.3.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e00c9d5c541a2713ba0e657e0303bf96ddddc412ef4761676adc35df35d7c246"}, + {file = "setproctitle-1.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:265ecbe2c6eafe82e104f994ddd7c811520acdd0647b73f65c24f51374cf9494"}, + {file = "setproctitle-1.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:c2c46200656280a064073447ebd363937562debef329482fd7e570c8d498f806"}, + {file = "setproctitle-1.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:fa2f50678f04fda7a75d0fe5dd02bbdd3b13cbe6ed4cf626e4472a7ccf47ae94"}, + {file = "setproctitle-1.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7f2719a398e1a2c01c2a63bf30377a34d0b6ef61946ab9cf4d550733af8f1ef1"}, + {file = "setproctitle-1.3.2-cp310-cp310-win32.whl", hash = "sha256:e425be62524dc0c593985da794ee73eb8a17abb10fe692ee43bb39e201d7a099"}, + {file = "setproctitle-1.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:e85e50b9c67854f89635a86247412f3ad66b132a4d8534ac017547197c88f27d"}, + {file = "setproctitle-1.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2a97d51c17d438cf5be284775a322d57b7ca9505bb7e118c28b1824ecaf8aeaa"}, + {file = "setproctitle-1.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:587c7d6780109fbd8a627758063d08ab0421377c0853780e5c356873cdf0f077"}, + {file = "setproctitle-1.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7d17c8bd073cbf8d141993db45145a70b307385b69171d6b54bcf23e5d644de"}, + {file = "setproctitle-1.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e932089c35a396dc31a5a1fc49889dd559548d14cb2237adae260382a090382e"}, + {file = "setproctitle-1.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8e4f8f12258a8739c565292a551c3db62cca4ed4f6b6126664e2381acb4931bf"}, + {file = "setproctitle-1.3.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:570d255fd99c7f14d8f91363c3ea96bd54f8742275796bca67e1414aeca7d8c3"}, + {file = "setproctitle-1.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a8e0881568c5e6beff91ef73c0ec8ac2a9d3ecc9edd6bd83c31ca34f770910c4"}, + {file = "setproctitle-1.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4bba3be4c1fabf170595b71f3af46c6d482fbe7d9e0563999b49999a31876f77"}, + {file = "setproctitle-1.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:37ece938110cab2bb3957e3910af8152ca15f2b6efdf4f2612e3f6b7e5459b80"}, + {file = "setproctitle-1.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:db684d6bbb735a80bcbc3737856385b55d53f8a44ce9b46e9a5682c5133a9bf7"}, + {file = "setproctitle-1.3.2-cp311-cp311-win32.whl", hash = "sha256:ca58cd260ea02759238d994cfae844fc8b1e206c684beb8f38877dcab8451dfc"}, + {file = "setproctitle-1.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:88486e6cce2a18a033013d17b30a594f1c5cb42520c49c19e6ade40b864bb7ff"}, + {file = "setproctitle-1.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:92c626edc66169a1b09e9541b9c0c9f10488447d8a2b1d87c8f0672e771bc927"}, + {file = "setproctitle-1.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:710e16fa3bade3b026907e4a5e841124983620046166f355bbb84be364bf2a02"}, + {file = "setproctitle-1.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1f29b75e86260b0ab59adb12661ef9f113d2f93a59951373eb6d68a852b13e83"}, + {file = "setproctitle-1.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c8d9650154afaa86a44ff195b7b10d683c73509d085339d174e394a22cccbb9"}, + {file = "setproctitle-1.3.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0452282258dfcc01697026a8841258dd2057c4438b43914b611bccbcd048f10"}, + {file = "setproctitle-1.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:e49ae693306d7624015f31cb3e82708916759d592c2e5f72a35c8f4cc8aef258"}, + {file = "setproctitle-1.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:1ff863a20d1ff6ba2c24e22436a3daa3cd80be1dfb26891aae73f61b54b04aca"}, + {file = "setproctitle-1.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:55ce1e9925ce1765865442ede9dca0ba9bde10593fcd570b1f0fa25d3ec6b31c"}, + {file = "setproctitle-1.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:7fe9df7aeb8c64db6c34fc3b13271a363475d77bc157d3f00275a53910cb1989"}, + {file = "setproctitle-1.3.2-cp37-cp37m-win32.whl", hash = "sha256:e5c50e164cd2459bc5137c15288a9ef57160fd5cbf293265ea3c45efe7870865"}, + {file = "setproctitle-1.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:a499fff50387c1520c085a07578a000123f519e5f3eee61dd68e1d301659651f"}, + {file = "setproctitle-1.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5b932c3041aa924163f4aab970c2f0e6b4d9d773f4d50326e0ea1cd69240e5c5"}, + {file = "setproctitle-1.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f4bfc89bd33ebb8e4c0e9846a09b1f5a4a86f5cb7a317e75cc42fee1131b4f4f"}, + {file = "setproctitle-1.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fcd3cf4286a60fdc95451d8d14e0389a6b4f5cebe02c7f2609325eb016535963"}, + {file = "setproctitle-1.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5fb4f769c02f63fac90989711a3fee83919f47ae9afd4758ced5d86596318c65"}, + {file = "setproctitle-1.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5194b4969f82ea842a4f6af2f82cd16ebdc3f1771fb2771796e6add9835c1973"}, + {file = "setproctitle-1.3.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f0cde41857a644b7353a0060b5f94f7ba7cf593ebde5a1094da1be581ac9a31"}, + {file = "setproctitle-1.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9124bedd8006b0e04d4e8a71a0945da9b67e7a4ab88fdad7b1440dc5b6122c42"}, + {file = "setproctitle-1.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:c8a09d570b39517de10ee5b718730e171251ce63bbb890c430c725c8c53d4484"}, + {file = "setproctitle-1.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:8ff3c8cb26afaed25e8bca7b9dd0c1e36de71f35a3a0706b5c0d5172587a3827"}, + {file = "setproctitle-1.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:589be87172b238f839e19f146b9ea47c71e413e951ef0dc6db4218ddacf3c202"}, + {file = "setproctitle-1.3.2-cp38-cp38-win32.whl", hash = "sha256:4749a2b0c9ac52f864d13cee94546606f92b981b50e46226f7f830a56a9dc8e1"}, + {file = "setproctitle-1.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:e43f315c68aa61cbdef522a2272c5a5b9b8fd03c301d3167b5e1343ef50c676c"}, + {file = "setproctitle-1.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:de3a540cd1817ede31f530d20e6a4935bbc1b145fd8f8cf393903b1e02f1ae76"}, + {file = "setproctitle-1.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4058564195b975ddc3f0462375c533cce310ccdd41b80ac9aed641c296c3eff4"}, + {file = "setproctitle-1.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c5d5dad7c28bdd1ec4187d818e43796f58a845aa892bb4481587010dc4d362b"}, + {file = "setproctitle-1.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ffc61a388a5834a97953d6444a2888c24a05f2e333f9ed49f977a87bb1ad4761"}, + {file = "setproctitle-1.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fa1a0fbee72b47dc339c87c890d3c03a72ea65c061ade3204f285582f2da30f"}, + {file = "setproctitle-1.3.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe8a988c7220c002c45347430993830666e55bc350179d91fcee0feafe64e1d4"}, + {file = "setproctitle-1.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bae283e85fc084b18ffeb92e061ff7ac5af9e183c9d1345c93e178c3e5069cbe"}, + {file = "setproctitle-1.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:fed18e44711c5af4b681c2b3b18f85e6f0f1b2370a28854c645d636d5305ccd8"}, + {file = "setproctitle-1.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:b34baef93bfb20a8ecb930e395ccd2ae3268050d8cf4fe187de5e2bd806fd796"}, + {file = "setproctitle-1.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7f0bed90a216ef28b9d227d8d73e28a8c9b88c0f48a082d13ab3fa83c581488f"}, + {file = "setproctitle-1.3.2-cp39-cp39-win32.whl", hash = "sha256:4d8938249a7cea45ab7e1e48b77685d0f2bab1ebfa9dde23e94ab97968996a7c"}, + {file = "setproctitle-1.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:a47d97a75fd2d10c37410b180f67a5835cb1d8fdea2648fd7f359d4277f180b9"}, + {file = "setproctitle-1.3.2-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:dad42e676c5261eb50fdb16bdf3e2771cf8f99a79ef69ba88729aeb3472d8575"}, + {file = "setproctitle-1.3.2-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c91b9bc8985d00239f7dc08a49927a7ca1ca8a6af2c3890feec3ed9665b6f91e"}, + {file = "setproctitle-1.3.2-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e8579a43eafd246e285eb3a5b939e7158073d5087aacdd2308f23200eac2458b"}, + {file = "setproctitle-1.3.2-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:2fbd8187948284293f43533c150cd69a0e4192c83c377da837dbcd29f6b83084"}, + {file = "setproctitle-1.3.2-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:faec934cfe5fd6ac1151c02e67156c3f526e82f96b24d550b5d51efa4a5527c6"}, + {file = "setproctitle-1.3.2-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e1aafc91cbdacc9e5fe712c52077369168e6b6c346f3a9d51bf600b53eae56bb"}, + {file = "setproctitle-1.3.2-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b617f12c9be61e8f4b2857be4a4319754756845dbbbd9c3718f468bbb1e17bcb"}, + {file = "setproctitle-1.3.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:b2c9cb2705fc84cb8798f1ba74194f4c080aaef19d9dae843591c09b97678e98"}, + {file = "setproctitle-1.3.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a149a5f7f2c5a065d4e63cb0d7a4b6d3b66e6e80f12e3f8827c4f63974cbf122"}, + {file = "setproctitle-1.3.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e3ac25bfc4a0f29d2409650c7532d5ddfdbf29f16f8a256fc31c47d0dc05172"}, + {file = "setproctitle-1.3.2-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65d884e22037b23fa25b2baf1a3316602ed5c5971eb3e9d771a38c3a69ce6e13"}, + {file = "setproctitle-1.3.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7aa0aac1711fadffc1d51e9d00a3bea61f68443d6ac0241a224e4d622489d665"}, + {file = "setproctitle-1.3.2.tar.gz", hash = "sha256:b9fb97907c830d260fa0658ed58afd48a86b2b88aac521135c352ff7fd3477fd"}, +] + +[package.extras] +test = ["pytest"] + +[[package]] +name = "setuptools" +version = "65.7.0" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "setuptools-65.7.0-py3-none-any.whl", hash = "sha256:8ab4f1dbf2b4a65f7eec5ad0c620e84c34111a68d3349833494b9088212214dd"}, + {file = "setuptools-65.7.0.tar.gz", hash = "sha256:4d3c92fac8f1118bb77a22181355e29c239cabfe2b9effdaa665c66b711136d7"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] + +[[package]] +name = "smmap" +version = "5.0.0" +description = "A pure Python implementation of a sliding window memory map manager" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "smmap-5.0.0-py3-none-any.whl", hash = "sha256:2aba19d6a040e78d8b09de5c57e96207b09ed71d8e55ce0959eeee6c8e190d94"}, + {file = "smmap-5.0.0.tar.gz", hash = "sha256:c840e62059cd3be204b0c9c9f74be2c09d5648eddd4580d9314c3ecde0b30936"}, +] + +[[package]] +name = "sniffio" +version = "1.3.0" +description = "Sniff out which async library your code is running under" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"}, + {file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"}, +] + +[[package]] +name = "soupsieve" +version = "2.3.2.post1" +description = "A modern CSS selector implementation for Beautiful Soup." +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "soupsieve-2.3.2.post1-py3-none-any.whl", hash = "sha256:3b2503d3c7084a42b1ebd08116e5f81aadfaea95863628c80a3b774a11b7c759"}, + {file = "soupsieve-2.3.2.post1.tar.gz", hash = "sha256:fc53893b3da2c33de295667a0e19f078c14bf86544af307354de5fcf12a3f30d"}, +] + +[[package]] +name = "stable-baselines3" +version = "1.7.0" +description = "Pytorch version of Stable Baselines, implementations of reinforcement learning algorithms." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "stable_baselines3-1.7.0-py3-none-any.whl", hash = "sha256:6cbb55918f15662e958a7088dd5effd230443b545c6f6a6248a3532c962dc1b0"}, + {file = "stable_baselines3-1.7.0.tar.gz", hash = "sha256:0b6d4b35b5b9060cf94bc09eb631a9a619d9a0d1eda06e690b0bf6336651f0a4"}, +] + +[package.dependencies] +ale-py = {version = "0.7.4", optional = true, markers = "extra == \"extra\""} +autorom = {version = ">=0.4.2,<0.5.0", extras = ["accept-rom-license"], optional = true, markers = "extra == \"extra\""} +cloudpickle = "*" +gym = "0.21" +importlib-metadata = ">=4.13,<5.0" +matplotlib = "*" +numpy = "*" +opencv-python = {version = "*", optional = true, markers = "extra == \"extra\""} +pandas = "*" +pillow = {version = "*", optional = true, markers = "extra == \"extra\""} +psutil = {version = "*", optional = true, markers = "extra == \"extra\""} +rich = {version = "*", optional = true, markers = "extra == \"extra\""} +tensorboard = {version = ">=2.9.1", optional = true, markers = "extra == \"extra\""} +torch = ">=1.11" +tqdm = {version = "*", optional = true, markers = "extra == \"extra\""} + +[package.extras] +docs = ["sphinx", "sphinx-autobuild", "sphinx-autodoc-typehints", "sphinx-copybutton", "sphinx-rtd-theme", "sphinxcontrib.spelling"] +extra = ["ale-py (==0.7.4)", "autorom[accept-rom-license] (>=0.4.2,<0.5.0)", "opencv-python", "pillow", "psutil", "rich", "tensorboard (>=2.9.1)", "tqdm"] +tests = ["black", "flake8 (>=3.8)", "flake8-bugbear", "isort (>=5.0)", "mypy", "pytest", "pytest-cov", "pytest-env", "pytest-xdist", "pytype", "scipy (>=1.4.1)"] + +[[package]] +name = "stack-data" +version = "0.6.2" +description = "Extract data from python stack frames and tracebacks for informative displays" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "stack_data-0.6.2-py3-none-any.whl", hash = "sha256:cbb2a53eb64e5785878201a97ed7c7b94883f48b87bfb0bbe8b623c74679e4a8"}, + {file = "stack_data-0.6.2.tar.gz", hash = "sha256:32d2dd0376772d01b6cb9fc996f3c8b57a357089dec328ed4b6553d037eaf815"}, +] + +[package.dependencies] +asttokens = ">=2.1.0" +executing = ">=1.2.0" +pure-eval = "*" + +[package.extras] +tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"] + +[[package]] +name = "tabulate" +version = "0.9.0" +description = "Pretty-print tabular data" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f"}, + {file = "tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c"}, +] + +[package.extras] +widechars = ["wcwidth"] + +[[package]] +name = "tensorboard" +version = "2.11.0" +description = "TensorBoard lets you watch Tensors Flow" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tensorboard-2.11.0-py3-none-any.whl", hash = "sha256:a0e592ee87962e17af3f0dce7faae3fbbd239030159e9e625cce810b7e35c53d"}, +] + +[package.dependencies] +absl-py = ">=0.4" +google-auth = ">=1.6.3,<3" +google-auth-oauthlib = ">=0.4.1,<0.5" +grpcio = ">=1.24.3" +markdown = ">=2.6.8" +numpy = ">=1.12.0" +protobuf = ">=3.9.2,<4" +requests = ">=2.21.0,<3" +setuptools = ">=41.0.0" +tensorboard-data-server = ">=0.6.0,<0.7.0" +tensorboard-plugin-wit = ">=1.6.0" +werkzeug = ">=1.0.1" +wheel = ">=0.26" + +[[package]] +name = "tensorboard-data-server" +version = "0.6.1" +description = "Fast data loading for TensorBoard" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "tensorboard_data_server-0.6.1-py3-none-any.whl", hash = "sha256:809fe9887682d35c1f7d1f54f0f40f98bb1f771b14265b453ca051e2ce58fca7"}, + {file = "tensorboard_data_server-0.6.1-py3-none-macosx_10_9_x86_64.whl", hash = "sha256:fa8cef9be4fcae2f2363c88176638baf2da19c5ec90addb49b1cde05c95c88ee"}, + {file = "tensorboard_data_server-0.6.1-py3-none-manylinux2010_x86_64.whl", hash = "sha256:d8237580755e58eff68d1f3abefb5b1e39ae5c8b127cc40920f9c4fb33f4b98a"}, +] + +[[package]] +name = "tensorboard-plugin-wit" +version = "1.8.1" +description = "What-If Tool TensorBoard plugin." +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "tensorboard_plugin_wit-1.8.1-py3-none-any.whl", hash = "sha256:ff26bdd583d155aa951ee3b152b3d0cffae8005dc697f72b44a8e8c2a77a8cbe"}, +] + +[[package]] +name = "terminado" +version = "0.17.1" +description = "Tornado websocket backend for the Xterm.js Javascript terminal emulator library." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "terminado-0.17.1-py3-none-any.whl", hash = "sha256:8650d44334eba354dd591129ca3124a6ba42c3d5b70df5051b6921d506fdaeae"}, + {file = "terminado-0.17.1.tar.gz", hash = "sha256:6ccbbcd3a4f8a25a5ec04991f39a0b8db52dfcd487ea0e578d977e6752380333"}, +] + +[package.dependencies] +ptyprocess = {version = "*", markers = "os_name != \"nt\""} +pywinpty = {version = ">=1.1.0", markers = "os_name == \"nt\""} +tornado = ">=6.1.0" + +[package.extras] +docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"] +test = ["pre-commit", "pytest (>=7.0)", "pytest-timeout"] + +[[package]] +name = "tinycss2" +version = "1.2.1" +description = "A tiny CSS parser" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tinycss2-1.2.1-py3-none-any.whl", hash = "sha256:2b80a96d41e7c3914b8cda8bc7f705a4d9c49275616e886103dd839dfc847847"}, + {file = "tinycss2-1.2.1.tar.gz", hash = "sha256:8cff3a8f066c2ec677c06dbc7b45619804a6938478d9d73c284b29d14ecb0627"}, +] + +[package.dependencies] +webencodings = ">=0.4" + +[package.extras] +doc = ["sphinx", "sphinx_rtd_theme"] +test = ["flake8", "isort", "pytest"] + +[[package]] +name = "tomli" +version = "2.0.1" +description = "A lil' TOML parser" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, +] + +[[package]] +name = "tomlkit" +version = "0.11.6" +description = "Style preserving TOML library" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "tomlkit-0.11.6-py3-none-any.whl", hash = "sha256:07de26b0d8cfc18f871aec595fda24d95b08fef89d147caa861939f37230bf4b"}, + {file = "tomlkit-0.11.6.tar.gz", hash = "sha256:71b952e5721688937fb02cf9d354dbcf0785066149d2855e44531ebdd2b65d73"}, +] + +[[package]] +name = "toolz" +version = "0.12.0" +description = "List processing tools and functional utilities" +category = "main" +optional = false +python-versions = ">=3.5" +files = [ + {file = "toolz-0.12.0-py3-none-any.whl", hash = "sha256:2059bd4148deb1884bb0eb770a3cde70e7f954cfbbdc2285f1f2de01fd21eb6f"}, + {file = "toolz-0.12.0.tar.gz", hash = "sha256:88c570861c440ee3f2f6037c4654613228ff40c93a6c25e0eba70d17282c6194"}, +] + +[[package]] +name = "torch" +version = "1.13.1" +description = "Tensors and Dynamic neural networks in Python with strong GPU acceleration" +category = "main" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "torch-1.13.1-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:fd12043868a34a8da7d490bf6db66991108b00ffbeecb034228bfcbbd4197143"}, + {file = "torch-1.13.1-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:d9fe785d375f2e26a5d5eba5de91f89e6a3be5d11efb497e76705fdf93fa3c2e"}, + {file = "torch-1.13.1-cp310-cp310-win_amd64.whl", hash = "sha256:98124598cdff4c287dbf50f53fb455f0c1e3a88022b39648102957f3445e9b76"}, + {file = "torch-1.13.1-cp310-none-macosx_10_9_x86_64.whl", hash = "sha256:393a6273c832e047581063fb74335ff50b4c566217019cc6ace318cd79eb0566"}, + {file = "torch-1.13.1-cp310-none-macosx_11_0_arm64.whl", hash = "sha256:0122806b111b949d21fa1a5f9764d1fd2fcc4a47cb7f8ff914204fd4fc752ed5"}, + {file = "torch-1.13.1-cp311-cp311-manylinux1_x86_64.whl", hash = "sha256:22128502fd8f5b25ac1cd849ecb64a418382ae81dd4ce2b5cebaa09ab15b0d9b"}, + {file = "torch-1.13.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:76024be052b659ac1304ab8475ab03ea0a12124c3e7626282c9c86798ac7bc11"}, + {file = "torch-1.13.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:ea8dda84d796094eb8709df0fcd6b56dc20b58fdd6bc4e8d7109930dafc8e419"}, + {file = "torch-1.13.1-cp37-cp37m-win_amd64.whl", hash = "sha256:2ee7b81e9c457252bddd7d3da66fb1f619a5d12c24d7074de91c4ddafb832c93"}, + {file = "torch-1.13.1-cp37-none-macosx_10_9_x86_64.whl", hash = "sha256:0d9b8061048cfb78e675b9d2ea8503bfe30db43d583599ae8626b1263a0c1380"}, + {file = "torch-1.13.1-cp37-none-macosx_11_0_arm64.whl", hash = "sha256:f402ca80b66e9fbd661ed4287d7553f7f3899d9ab54bf5c67faada1555abde28"}, + {file = "torch-1.13.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:727dbf00e2cf858052364c0e2a496684b9cb5aa01dc8a8bc8bbb7c54502bdcdd"}, + {file = "torch-1.13.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:df8434b0695e9ceb8cc70650afc1310d8ba949e6db2a0525ddd9c3b2b181e5fe"}, + {file = "torch-1.13.1-cp38-cp38-win_amd64.whl", hash = "sha256:5e1e722a41f52a3f26f0c4fcec227e02c6c42f7c094f32e49d4beef7d1e213ea"}, + {file = "torch-1.13.1-cp38-none-macosx_10_9_x86_64.whl", hash = "sha256:33e67eea526e0bbb9151263e65417a9ef2d8fa53cbe628e87310060c9dcfa312"}, + {file = "torch-1.13.1-cp38-none-macosx_11_0_arm64.whl", hash = "sha256:eeeb204d30fd40af6a2d80879b46a7efbe3cf43cdbeb8838dd4f3d126cc90b2b"}, + {file = "torch-1.13.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:50ff5e76d70074f6653d191fe4f6a42fdbe0cf942fbe2a3af0b75eaa414ac038"}, + {file = "torch-1.13.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:2c3581a3fd81eb1f0f22997cddffea569fea53bafa372b2c0471db373b26aafc"}, + {file = "torch-1.13.1-cp39-cp39-win_amd64.whl", hash = "sha256:0aa46f0ac95050c604bcf9ef71da9f1172e5037fdf2ebe051962d47b123848e7"}, + {file = "torch-1.13.1-cp39-none-macosx_10_9_x86_64.whl", hash = "sha256:6930791efa8757cb6974af73d4996b6b50c592882a324b8fb0589c6a9ba2ddaf"}, + {file = "torch-1.13.1-cp39-none-macosx_11_0_arm64.whl", hash = "sha256:e0df902a7c7dd6c795698532ee5970ce898672625635d885eade9976e5a04949"}, +] + +[package.dependencies] +nvidia-cublas-cu11 = {version = "11.10.3.66", markers = "platform_system == \"Linux\""} +nvidia-cuda-nvrtc-cu11 = {version = "11.7.99", markers = "platform_system == \"Linux\""} +nvidia-cuda-runtime-cu11 = {version = "11.7.99", markers = "platform_system == \"Linux\""} +nvidia-cudnn-cu11 = {version = "8.5.0.96", markers = "platform_system == \"Linux\""} +typing-extensions = "*" + +[package.extras] +opt-einsum = ["opt-einsum (>=3.3)"] + +[[package]] +name = "torch-tb-profiler" +version = "0.4.1" +description = "PyTorch Profiler TensorBoard Plugin" +category = "main" +optional = false +python-versions = ">=3.6.2" +files = [ + {file = "torch_tb_profiler-0.4.1-py3-none-any.whl", hash = "sha256:df7428ce5564e8357d0d03c0f246398c448fc8cd91b3075370ca5c25defbc635"}, + {file = "torch_tb_profiler-0.4.1.tar.gz", hash = "sha256:f2c7fb27d420be443ffde50ada655c19f76a245d21e7772de753196fd0967685"}, +] + +[package.dependencies] +pandas = ">=1.0.0" +tensorboard = ">=1.15,<2.1.0 || >2.1.0" + +[package.extras] +blob = ["azure-storage-blob"] +gs = ["google-cloud-storage"] +s3 = ["boto3"] + +[[package]] +name = "tornado" +version = "6.2" +description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." +category = "main" +optional = false +python-versions = ">= 3.7" +files = [ + {file = "tornado-6.2-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:20f638fd8cc85f3cbae3c732326e96addff0a15e22d80f049e00121651e82e72"}, + {file = "tornado-6.2-cp37-abi3-macosx_10_9_x86_64.whl", hash = "sha256:87dcafae3e884462f90c90ecc200defe5e580a7fbbb4365eda7c7c1eb809ebc9"}, + {file = "tornado-6.2-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba09ef14ca9893954244fd872798b4ccb2367c165946ce2dd7376aebdde8e3ac"}, + {file = "tornado-6.2-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b8150f721c101abdef99073bf66d3903e292d851bee51910839831caba341a75"}, + {file = "tornado-6.2-cp37-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3a2f5999215a3a06a4fc218026cd84c61b8b2b40ac5296a6db1f1451ef04c1e"}, + {file = "tornado-6.2-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:5f8c52d219d4995388119af7ccaa0bcec289535747620116a58d830e7c25d8a8"}, + {file = "tornado-6.2-cp37-abi3-musllinux_1_1_i686.whl", hash = "sha256:6fdfabffd8dfcb6cf887428849d30cf19a3ea34c2c248461e1f7d718ad30b66b"}, + {file = "tornado-6.2-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:1d54d13ab8414ed44de07efecb97d4ef7c39f7438cf5e976ccd356bebb1b5fca"}, + {file = "tornado-6.2-cp37-abi3-win32.whl", hash = "sha256:5c87076709343557ef8032934ce5f637dbb552efa7b21d08e89ae7619ed0eb23"}, + {file = "tornado-6.2-cp37-abi3-win_amd64.whl", hash = "sha256:e5f923aa6a47e133d1cf87d60700889d7eae68988704e20c75fb2d65677a8e4b"}, + {file = "tornado-6.2.tar.gz", hash = "sha256:9b630419bde84ec666bfd7ea0a4cb2a8a651c2d5cccdbdd1972a0c859dfc3c13"}, +] + +[[package]] +name = "tqdm" +version = "4.64.1" +description = "Fast, Extensible Progress Meter" +category = "main" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" +files = [ + {file = "tqdm-4.64.1-py2.py3-none-any.whl", hash = "sha256:6fee160d6ffcd1b1c68c65f14c829c22832bc401726335ce92c52d395944a6a1"}, + {file = "tqdm-4.64.1.tar.gz", hash = "sha256:5f4f682a004951c1b450bc753c710e9280c5746ce6ffedee253ddbcbf54cf1e4"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[package.extras] +dev = ["py-make (>=0.1.0)", "twine", "wheel"] +notebook = ["ipywidgets (>=6)"] +slack = ["slack-sdk"] +telegram = ["requests"] + +[[package]] +name = "traitlets" +version = "5.8.1" +description = "Traitlets Python configuration system" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "traitlets-5.8.1-py3-none-any.whl", hash = "sha256:a1ca5df6414f8b5760f7c5f256e326ee21b581742114545b462b35ffe3f04861"}, + {file = "traitlets-5.8.1.tar.gz", hash = "sha256:32500888f5ff7bbf3b9267ea31748fa657aaf34d56d85e60f91dda7dc7f5785b"}, +] + +[package.extras] +docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"] +test = ["argcomplete (>=2.0)", "pre-commit", "pytest", "pytest-mock"] + +[[package]] +name = "typing-extensions" +version = "4.4.0" +description = "Backported and Experimental Type Hints for Python 3.7+" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "typing_extensions-4.4.0-py3-none-any.whl", hash = "sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e"}, + {file = "typing_extensions-4.4.0.tar.gz", hash = "sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa"}, +] + +[[package]] +name = "uri-template" +version = "1.2.0" +description = "RFC 6570 URI Template Processor" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "uri_template-1.2.0-py3-none-any.whl", hash = "sha256:f1699c77b73b925cf4937eae31ab282a86dc885c333f2e942513f08f691fc7db"}, + {file = "uri_template-1.2.0.tar.gz", hash = "sha256:934e4d09d108b70eb8a24410af8615294d09d279ce0e7cbcdaef1bd21f932b06"}, +] + +[package.extras] +dev = ["flake8 (<4.0.0)", "flake8-annotations", "flake8-bugbear", "flake8-commas", "flake8-comprehensions", "flake8-continuation", "flake8-datetimez", "flake8-docstrings", "flake8-import-order", "flake8-literal", "flake8-noqa", "flake8-requirements", "flake8-type-annotations", "flake8-use-fstring", "mypy", "pep8-naming"] + +[[package]] +name = "urllib3" +version = "1.26.14" +description = "HTTP library with thread-safe connection pooling, file post, and more." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +files = [ + {file = "urllib3-1.26.14-py2.py3-none-any.whl", hash = "sha256:75edcdc2f7d85b137124a6c3c9fc3933cdeaa12ecb9a6a959f22797a0feca7e1"}, + {file = "urllib3-1.26.14.tar.gz", hash = "sha256:076907bf8fd355cde77728471316625a4d2f7e713c125f51953bb5b3eecf4f72"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] +secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] +socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] + +[[package]] +name = "virtualenv" +version = "20.17.1" +description = "Virtual Python Environment builder" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "virtualenv-20.17.1-py3-none-any.whl", hash = "sha256:ce3b1684d6e1a20a3e5ed36795a97dfc6af29bc3970ca8dab93e11ac6094b3c4"}, + {file = "virtualenv-20.17.1.tar.gz", hash = "sha256:f8b927684efc6f1cc206c9db297a570ab9ad0e51c16fa9e45487d36d1905c058"}, +] + +[package.dependencies] +distlib = ">=0.3.6,<1" +filelock = ">=3.4.1,<4" +platformdirs = ">=2.4,<3" + +[package.extras] +docs = ["proselint (>=0.13)", "sphinx (>=5.3)", "sphinx-argparse (>=0.3.2)", "sphinx-rtd-theme (>=1)", "towncrier (>=22.8)"] +testing = ["coverage (>=6.2)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=21.3)", "pytest (>=7.0.1)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.2)", "pytest-mock (>=3.6.1)", "pytest-randomly (>=3.10.3)", "pytest-timeout (>=2.1)"] + +[[package]] +name = "wandb" +version = "0.13.10" +description = "A CLI and library for interacting with the Weights and Biases API." +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "wandb-0.13.10-py3-none-any.whl", hash = "sha256:d4a3397aed2790d5fa8adc5336e3607813dc498a49b1a0a48297f78d89a71b0b"}, + {file = "wandb-0.13.10.tar.gz", hash = "sha256:a2cde45dd6390ed1923ef3e1eca26487cb1e02e4732515856933c4494a975ad3"}, +] + +[package.dependencies] +appdirs = ">=1.4.3" +Click = ">=7.0,<8.0.0 || >8.0.0" +docker-pycreds = ">=0.4.0" +GitPython = ">=1.0.0" +pathtools = "*" +protobuf = {version = ">=3.19.0,<4.21.0 || >4.21.0,<5", markers = "python_version > \"3.9\" or sys_platform != \"linux\""} +psutil = ">=5.0.0" +PyYAML = "*" +requests = ">=2.0.0,<3" +sentry-sdk = ">=1.0.0" +setproctitle = "*" +setuptools = "*" + +[package.extras] +aws = ["boto3"] +azure = ["azure-storage-blob"] +gcp = ["google-cloud-storage"] +grpc = ["grpcio (>=1.27.2)"] +kubeflow = ["google-cloud-storage", "kubernetes", "minio", "sh"] +launch = ["boto3", "botocore", "chardet", "google-cloud-storage", "iso8601", "kubernetes", "nbconvert", "nbformat", "typing-extensions"] +media = ["bokeh", "moviepy", "numpy", "pillow", "plotly", "rdkit-pypi", "soundfile"] +models = ["cloudpickle"] +sweeps = ["sweeps (>=0.2.0)"] + +[[package]] +name = "wcwidth" +version = "0.2.6" +description = "Measures the displayed width of unicode strings in a terminal" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "wcwidth-0.2.6-py2.py3-none-any.whl", hash = "sha256:795b138f6875577cd91bba52baf9e445cd5118fd32723b460e30a0af30ea230e"}, + {file = "wcwidth-0.2.6.tar.gz", hash = "sha256:a5220780a404dbe3353789870978e472cfe477761f06ee55077256e509b156d0"}, +] + +[[package]] +name = "webcolors" +version = "1.12" +description = "A library for working with color names and color values formats defined by HTML and CSS." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "webcolors-1.12-py3-none-any.whl", hash = "sha256:d98743d81d498a2d3eaf165196e65481f0d2ea85281463d856b1e51b09f62dce"}, + {file = "webcolors-1.12.tar.gz", hash = "sha256:16d043d3a08fd6a1b1b7e3e9e62640d09790dce80d2bdd4792a175b35fe794a9"}, +] + +[[package]] +name = "webencodings" +version = "0.5.1" +description = "Character encoding aliases for legacy web content" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"}, + {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"}, +] + +[[package]] +name = "websocket-client" +version = "1.4.2" +description = "WebSocket client for Python with low level API options" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "websocket-client-1.4.2.tar.gz", hash = "sha256:d6e8f90ca8e2dd4e8027c4561adeb9456b54044312dba655e7cae652ceb9ae59"}, + {file = "websocket_client-1.4.2-py3-none-any.whl", hash = "sha256:d6b06432f184438d99ac1f456eaf22fe1ade524c3dd16e661142dc54e9cba574"}, +] + +[package.extras] +docs = ["Sphinx (>=3.4)", "sphinx-rtd-theme (>=0.5)"] +optional = ["python-socks", "wsaccel"] +test = ["websockets"] + +[[package]] +name = "werkzeug" +version = "2.2.2" +description = "The comprehensive WSGI web application library." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "Werkzeug-2.2.2-py3-none-any.whl", hash = "sha256:f979ab81f58d7318e064e99c4506445d60135ac5cd2e177a2de0089bfd4c9bd5"}, + {file = "Werkzeug-2.2.2.tar.gz", hash = "sha256:7ea2d48322cc7c0f8b3a215ed73eabd7b5d75d0b50e31ab006286ccff9e00b8f"}, +] + +[package.dependencies] +MarkupSafe = ">=2.1.1" + +[package.extras] +watchdog = ["watchdog"] + +[[package]] +name = "wheel" +version = "0.38.4" +description = "A built-package format for Python" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "wheel-0.38.4-py3-none-any.whl", hash = "sha256:b60533f3f5d530e971d6737ca6d58681ee434818fab630c83a734bb10c083ce8"}, + {file = "wheel-0.38.4.tar.gz", hash = "sha256:965f5259b566725405b05e7cf774052044b1ed30119b5d586b2703aafe8719ac"}, +] + +[package.extras] +test = ["pytest (>=3.0.0)"] + +[[package]] +name = "widgetsnbextension" +version = "4.0.5" +description = "Jupyter interactive widgets for Jupyter Notebook" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "widgetsnbextension-4.0.5-py3-none-any.whl", hash = "sha256:eaaaf434fb9b08bd197b2a14ffe45ddb5ac3897593d43c69287091e5f3147bf7"}, + {file = "widgetsnbextension-4.0.5.tar.gz", hash = "sha256:003f716d930d385be3fd9de42dd9bf008e30053f73bddde235d14fbeaeff19af"}, +] + +[[package]] +name = "zipp" +version = "3.11.0" +description = "Backport of pathlib-compatible object wrapper for zip files" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "zipp-3.11.0-py3-none-any.whl", hash = "sha256:83a28fcb75844b5c0cdaf5aa4003c2d728c77e05f5aeabe8e95e56727005fbaa"}, + {file = "zipp-3.11.0.tar.gz", hash = "sha256:a7a22e05929290a67401440b39690ae6563279bced5f314609d9d03798f56766"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"] +testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] + +[metadata] +lock-version = "2.0" +python-versions = "~3.10" +content-hash = "ab3cf6f3768a3f2d608f049b0ee69c9224dd1530f0bd392a4116f23597aaf34a" diff --git a/ppo/ppo.py b/ppo/ppo.py new file mode 100644 index 0000000000000000000000000000000000000000..0daae26ebb77c009cf9c9af51b802fe1efcfdd10 --- /dev/null +++ b/ppo/ppo.py @@ -0,0 +1,349 @@ +import numpy as np +import torch +import torch.nn as nn +import torch.nn.functional as F + +from dataclasses import asdict, dataclass, field +from time import perf_counter +from torch.optim import Adam +from torch.utils.tensorboard.writer import SummaryWriter +from typing import List, Optional, NamedTuple, TypeVar + +from shared.algorithm import Algorithm +from shared.callbacks.callback import Callback +from shared.gae import compute_advantage, compute_rtg_and_advantage +from shared.policy.on_policy import ActorCritic +from shared.schedule import constant_schedule, linear_schedule, update_learning_rate +from shared.trajectory import Trajectory, TrajectoryAccumulator +from wrappers.vectorable_wrapper import VecEnv, VecEnvObs + + +@dataclass +class PPOTrajectory(Trajectory): + logp_a: List[float] = field(default_factory=list) + + def add( + self, + obs: np.ndarray, + act: np.ndarray, + next_obs: np.ndarray, + rew: float, + terminated: bool, + v: float, + logp_a: float, + ): + super().add(obs, act, next_obs, rew, terminated, v) + self.logp_a.append(logp_a) + + +class PPOTrajectoryAccumulator(TrajectoryAccumulator): + def __init__(self, num_envs: int) -> None: + super().__init__(num_envs, PPOTrajectory) + + def step( + self, + obs: VecEnvObs, + action: np.ndarray, + next_obs: VecEnvObs, + reward: np.ndarray, + done: np.ndarray, + val: np.ndarray, + logp_a: np.ndarray, + ) -> None: + super().step(obs, action, next_obs, reward, done, val, logp_a) + + +class TrainStepStats(NamedTuple): + loss: float + pi_loss: float + v_loss: float + entropy_loss: float + approx_kl: float + clipped_frac: float + val_clipped_frac: float + + +@dataclass +class TrainStats: + loss: float + pi_loss: float + v_loss: float + entropy_loss: float + approx_kl: float + clipped_frac: float + val_clipped_frac: float + explained_var: float + + def __init__(self, step_stats: List[TrainStepStats], explained_var: float) -> None: + self.loss = np.mean([s.loss for s in step_stats]).item() + self.pi_loss = np.mean([s.pi_loss for s in step_stats]).item() + self.v_loss = np.mean([s.v_loss for s in step_stats]).item() + self.entropy_loss = np.mean([s.entropy_loss for s in step_stats]).item() + self.approx_kl = np.mean([s.approx_kl for s in step_stats]).item() + self.clipped_frac = np.mean([s.clipped_frac for s in step_stats]).item() + self.val_clipped_frac = np.mean([s.val_clipped_frac for s in step_stats]).item() + self.explained_var = explained_var + + def write_to_tensorboard(self, tb_writer: SummaryWriter, global_step: int) -> None: + for name, value in asdict(self).items(): + tb_writer.add_scalar(f"losses/{name}", value, global_step=global_step) + + def __repr__(self) -> str: + return " | ".join( + [ + f"Loss: {round(self.loss, 2)}", + f"Pi L: {round(self.pi_loss, 2)}", + f"V L: {round(self.v_loss, 2)}", + f"E L: {round(self.entropy_loss, 2)}", + f"Apx KL Div: {round(self.approx_kl, 2)}", + f"Clip Frac: {round(self.clipped_frac, 2)}", + f"Val Clip Frac: {round(self.val_clipped_frac, 2)}", + ] + ) + + +PPOSelf = TypeVar("PPOSelf", bound="PPO") + + +class PPO(Algorithm): + def __init__( + self, + policy: ActorCritic, + env: VecEnv, + device: torch.device, + tb_writer: SummaryWriter, + learning_rate: float = 3e-4, + learning_rate_decay: str = "none", + n_steps: int = 2048, + batch_size: int = 64, + n_epochs: int = 10, + gamma: float = 0.99, + gae_lambda: float = 0.95, + clip_range: float = 0.2, + clip_range_decay: str = "none", + clip_range_vf: Optional[float] = None, + clip_range_vf_decay: str = "none", + normalize_advantage: bool = True, + ent_coef: float = 0.0, + ent_coef_decay: str = "none", + vf_coef: float = 0.5, + ppo2_vf_coef_halving: bool = False, + max_grad_norm: float = 0.5, + update_rtg_between_epochs: bool = False, + sde_sample_freq: int = -1, + ) -> None: + super().__init__(policy, env, device, tb_writer) + self.policy = policy + + self.gamma = gamma + self.gae_lambda = gae_lambda + self.optimizer = Adam(self.policy.parameters(), lr=learning_rate, eps=1e-7) + self.lr_schedule = ( + linear_schedule(learning_rate, 0) + if learning_rate_decay == "linear" + else constant_schedule(learning_rate) + ) + self.max_grad_norm = max_grad_norm + self.clip_range_schedule = ( + linear_schedule(clip_range, 0) + if clip_range_decay == "linear" + else constant_schedule(clip_range) + ) + self.clip_range_vf_schedule = None + if clip_range_vf: + self.clip_range_vf_schedule = ( + linear_schedule(clip_range_vf, 0) + if clip_range_vf_decay == "linear" + else constant_schedule(clip_range_vf) + ) + self.normalize_advantage = normalize_advantage + self.ent_coef_schedule = ( + linear_schedule(ent_coef, 0) + if ent_coef_decay == "linear" + else constant_schedule(ent_coef) + ) + self.vf_coef = vf_coef + self.ppo2_vf_coef_halving = ppo2_vf_coef_halving + + self.n_steps = n_steps + self.batch_size = batch_size + self.n_epochs = n_epochs + self.sde_sample_freq = sde_sample_freq + + self.update_rtg_between_epochs = update_rtg_between_epochs + + def learn( + self: PPOSelf, + total_timesteps: int, + callback: Optional[Callback] = None, + ) -> PPOSelf: + obs = self.env.reset() + ts_elapsed = 0 + while ts_elapsed < total_timesteps: + start_time = perf_counter() + accumulator = self._collect_trajectories(obs) + rollout_steps = self.n_steps * self.env.num_envs + ts_elapsed += rollout_steps + progress = ts_elapsed / total_timesteps + train_stats = self.train(accumulator.all_trajectories, progress, ts_elapsed) + train_stats.write_to_tensorboard(self.tb_writer, ts_elapsed) + end_time = perf_counter() + self.tb_writer.add_scalar( + "train/steps_per_second", + rollout_steps / (end_time - start_time), + ts_elapsed, + ) + if callback: + callback.on_step(timesteps_elapsed=rollout_steps) + + return self + + def _collect_trajectories(self, obs: VecEnvObs) -> PPOTrajectoryAccumulator: + self.policy.eval() + accumulator = PPOTrajectoryAccumulator(self.env.num_envs) + self.policy.reset_noise() + for i in range(self.n_steps): + if self.sde_sample_freq > 0 and i > 0 and i % self.sde_sample_freq == 0: + self.policy.reset_noise() + action, value, logp_a, clamped_action = self.policy.step(obs) + next_obs, reward, done, _ = self.env.step(clamped_action) + accumulator.step(obs, action, next_obs, reward, done, value, logp_a) + obs = next_obs + return accumulator + + def train( + self, trajectories: List[PPOTrajectory], progress: float, timesteps_elapsed: int + ) -> TrainStats: + self.policy.train() + learning_rate = self.lr_schedule(progress) + update_learning_rate(self.optimizer, learning_rate) + self.tb_writer.add_scalar( + "charts/learning_rate", + self.optimizer.param_groups[0]["lr"], + timesteps_elapsed, + ) + + pi_clip = self.clip_range_schedule(progress) + self.tb_writer.add_scalar("charts/pi_clip", pi_clip, timesteps_elapsed) + if self.clip_range_vf_schedule: + v_clip = self.clip_range_vf_schedule(progress) + self.tb_writer.add_scalar("charts/v_clip", v_clip, timesteps_elapsed) + else: + v_clip = None + ent_coef = self.ent_coef_schedule(progress) + self.tb_writer.add_scalar("charts/ent_coef", ent_coef, timesteps_elapsed) + + obs = torch.as_tensor( + np.concatenate([np.array(t.obs) for t in trajectories]), device=self.device + ) + act = torch.as_tensor( + np.concatenate([np.array(t.act) for t in trajectories]), device=self.device + ) + rtg, adv = compute_rtg_and_advantage( + trajectories, self.policy, self.gamma, self.gae_lambda, self.device + ) + orig_v = torch.as_tensor( + np.concatenate([np.array(t.v) for t in trajectories]), device=self.device + ) + orig_logp_a = torch.as_tensor( + np.concatenate([np.array(t.logp_a) for t in trajectories]), + device=self.device, + ) + + step_stats = [] + for _ in range(self.n_epochs): + step_stats.clear() + if self.update_rtg_between_epochs: + rtg, adv = compute_rtg_and_advantage( + trajectories, self.policy, self.gamma, self.gae_lambda, self.device + ) + else: + adv = compute_advantage( + trajectories, self.policy, self.gamma, self.gae_lambda, self.device + ) + idxs = torch.randperm(len(obs)) + for i in range(0, len(obs), self.batch_size): + mb_idxs = idxs[i : i + self.batch_size] + mb_adv = adv[mb_idxs] + if self.normalize_advantage: + mb_adv = (mb_adv - mb_adv.mean(-1)) / (mb_adv.std(-1) + 1e-8) + step_stats.append( + self._train_step( + pi_clip, + v_clip, + ent_coef, + obs[mb_idxs], + act[mb_idxs], + rtg[mb_idxs], + mb_adv, + orig_v[mb_idxs], + orig_logp_a[mb_idxs], + ) + ) + + y_pred, y_true = orig_v.cpu().numpy(), rtg.cpu().numpy() + var_y = np.var(y_true).item() + explained_var = ( + np.nan if var_y == 0 else 1 - np.var(y_true - y_pred).item() / var_y + ) + + return TrainStats(step_stats, explained_var) + + def _train_step( + self, + pi_clip: float, + v_clip: Optional[float], + ent_coef: float, + obs: torch.Tensor, + act: torch.Tensor, + rtg: torch.Tensor, + adv: torch.Tensor, + orig_v: torch.Tensor, + orig_logp_a: torch.Tensor, + ) -> TrainStepStats: + logp_a, entropy, v = self.policy(obs, act) + logratio = logp_a - orig_logp_a + ratio = torch.exp(logratio) + clip_ratio = torch.clamp(ratio, min=1 - pi_clip, max=1 + pi_clip) + pi_loss = torch.maximum(-ratio * adv, -clip_ratio * adv).mean() + + v_loss_unclipped = (v - rtg) ** 2 + if v_clip: + v_loss_clipped = ( + orig_v + torch.clamp(v - orig_v, -v_clip, v_clip) - rtg + ) ** 2 + v_loss = torch.max(v_loss_unclipped, v_loss_clipped).mean() + else: + v_loss = v_loss_unclipped.mean() + if self.ppo2_vf_coef_halving: + v_loss *= 0.5 + + entropy_loss = -entropy.mean() + + loss = pi_loss + ent_coef * entropy_loss + self.vf_coef * v_loss + + self.optimizer.zero_grad() + loss.backward() + nn.utils.clip_grad_norm_(self.policy.parameters(), self.max_grad_norm) + self.optimizer.step() + + with torch.no_grad(): + approx_kl = ((ratio - 1) - logratio).mean().cpu().numpy().item() + clipped_frac = ( + ((ratio - 1).abs() > pi_clip).float().mean().cpu().numpy().item() + ) + val_clipped_frac = ( + (((v - orig_v).abs() > v_clip).float().mean().cpu().numpy().item()) + if v_clip + else 0 + ) + + return TrainStepStats( + loss.item(), + pi_loss.item(), + v_loss.item(), + entropy_loss.item(), + approx_kl, + clipped_frac, + val_clipped_frac, + ) diff --git a/publish/markdown_format.py b/publish/markdown_format.py new file mode 100644 index 0000000000000000000000000000000000000000..1e48fd1ef85ce19ad952b04433911b3fe11bf395 --- /dev/null +++ b/publish/markdown_format.py @@ -0,0 +1,210 @@ +import os +import pandas as pd +import wandb.apis.public +import yaml + +from collections import defaultdict +from dataclasses import dataclass, asdict +from typing import Any, Dict, Iterable, List, NamedTuple, Optional, TypeVar +from urllib.parse import urlparse + +from runner.evaluate import Evaluation + +EvaluationRowSelf = TypeVar("EvaluationRowSelf", bound="EvaluationRow") + + +@dataclass +class EvaluationRow: + algo: str + env: str + seed: Optional[int] + reward_mean: float + reward_std: float + eval_episodes: int + best: str + wandb_url: str + + @staticmethod + def data_frame(rows: List[EvaluationRowSelf]) -> pd.DataFrame: + results = defaultdict(list) + for r in rows: + for k, v in asdict(r).items(): + results[k].append(v) + return pd.DataFrame(results) + + +class EvalTableData(NamedTuple): + run: wandb.apis.public.Run + evaluation: Evaluation + + +def evaluation_table(table_data: Iterable[EvalTableData]) -> str: + best_stats = sorted( + [d.evaluation.stats for d in table_data], key=lambda r: r.score, reverse=True + )[0] + table_data = sorted(table_data, key=lambda d: d.evaluation.config.seed() or 0) + rows = [ + EvaluationRow( + config.algo, + config.env_id, + config.seed(), + stats.score.mean, + stats.score.std, + len(stats), + "*" if stats == best_stats else "", + f"[wandb]({r.url})", + ) + for (r, (_, stats, config)) in table_data + ] + df = EvaluationRow.data_frame(rows) + return df.to_markdown(index=False) + + +def github_project_link(github_url: str) -> str: + return f"[{urlparse(github_url).path}]({github_url})" + + +def header_section(algo: str, env: str, github_url: str, wandb_report_url: str) -> str: + algo_caps = algo.upper() + lines = [ + f"# **{algo_caps}** Agent playing **{env}**", + f"This is a trained model of a **{algo_caps}** agent playing **{env}** using " + f"the {github_project_link(github_url)} repo.", + f"All models trained at this commit can be found at {wandb_report_url}.", + ] + return "\n\n".join(lines) + + +def github_tree_link(github_url: str, commit_hash: Optional[str]) -> str: + if not commit_hash: + return github_project_link(github_url) + return f"[{commit_hash[:7]}]({github_url}/tree/{commit_hash})" + + +def results_section( + table_data: List[EvalTableData], algo: str, github_url: str, commit_hash: str +) -> str: + # type: ignore + lines = [ + "## Training Results", + f"This model was trained from {len(table_data)} trainings of **{algo.upper()}** " + + "agents using different initial seeds. " + + f"These agents were trained by checking out " + + f"{github_tree_link(github_url, commit_hash)}. " + + "The best and last models were kept from each training. " + + "This submission has loaded the best models from each training, reevaluates " + + "them, and selects the best model from these latest evaluations (mean - std).", + ] + lines.append(evaluation_table(table_data)) + return "\n\n".join(lines) + + +def prerequisites_section() -> str: + return """ +### Prerequisites: Weights & Biases (WandB) +Training and benchmarking assumes you have a Weights & Biases project to upload runs to. +By default training goes to a rl-algo-impls project while benchmarks go to +rl-algo-impls-benchmarks. During training and benchmarking runs, videos of the best +models and the model weights are uploaded to WandB. + +Before doing anything below, you'll need to create a wandb account and run `wandb +login`. +""" + + +def usage_section(github_url: str, run_path: str, commit_hash: str) -> str: + return f""" +## Usage +{urlparse(github_url).path}: {github_url} + +Note: While the model state dictionary and hyperaparameters are saved, the latest +implementation could be sufficiently different to not be able to reproduce similar +results. You might need to checkout the commit the agent was trained on: +{github_tree_link(github_url, commit_hash)}. +``` +# Downloads the model, sets hyperparameters, and runs agent for 3 episodes +python enjoy.py --wandb-run-path={run_path} +``` + +Setup hasn't been completely worked out yet, so you might be best served by using Google +Colab starting from the +[colab_enjoy.ipynb](https://github.com/sgoodfriend/rl-algo-impls/blob/main/colab_enjoy.ipynb) +notebook. +""" + + +def training_setion( + github_url: str, commit_hash: str, algo: str, env: str, seed: Optional[int] +) -> str: + return f""" +## Training +If you want the highest chance to reproduce these results, you'll want to checkout the +commit the agent was trained on: {github_tree_link(github_url, commit_hash)}. While +training is deterministic, different hardware will give different results. + +``` +python train.py --algo {algo} --env {env} {'--seed ' + str(seed) if seed is not None else ''} +``` + +Setup hasn't been completely worked out yet, so you might be best served by using Google +Colab starting from the +[colab_train.ipynb](https://github.com/sgoodfriend/rl-algo-impls/blob/main/colab_train.ipynb) +notebook. +""" + + +def benchmarking_section(report_url: str) -> str: + return f""" +## Benchmarking (with Lambda Labs instance) +This and other models from {report_url} were generated by running a script on a Lambda +Labs instance. In a Lambda Labs instance terminal: +``` +git clone git@github.com:sgoodfriend/rl-algo-impls.git +cd rl-algo-impls +bash ./lambda_labs/setup.sh +wandb login +bash ./lambda_labs/benchmark.sh +``` + +### Alternative: Google Colab Pro+ +As an alternative, +[colab_benchmark.ipynb](https://github.com/sgoodfriend/rl-algo-impls/tree/main/benchmarks#:~:text=colab_benchmark.ipynb), +can be used. However, this requires a Google Colab Pro+ subscription and running across +4 separate instances because otherwise running all jobs will exceed the 24-hour limit. +""" + + +def hyperparams_section(run_config: Dict[str, Any]) -> str: + return f""" +## Hyperparameters +This isn't exactly the format of hyperparams in {os.path.join("hyperparams", +run_config["algo"] + ".yml")}, but instead the Wandb Run Config. However, it's very +close and has some additional data: +``` +{yaml.dump(run_config)} +``` +""" + + +def model_card_text( + algo: str, + env: str, + github_url: str, + commit_hash: str, + wandb_report_url: str, + table_data: List[EvalTableData], + best_eval: EvalTableData, +) -> str: + run, (_, _, config) = best_eval + run_path = "/".join(run.path) + return "\n\n".join( + [ + header_section(algo, env, github_url, wandb_report_url), + results_section(table_data, algo, github_url, commit_hash), + prerequisites_section(), + usage_section(github_url, run_path, commit_hash), + training_setion(github_url, commit_hash, algo, env, config.seed()), + benchmarking_section(wandb_report_url), + hyperparams_section(run.config), + ] + ) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000000000000000000000000000000000000..d5c87129c896ce8f5ac2e32d63183d7fc206bd4c --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,35 @@ +[tool.poetry] +name = "rl-algo-impls" +version = "0.1.0" +description = "Implementations of reinforcement learning algorithms" +authors = ["Scott Goodfriend "] +license = "MIT License" +readme = "README.md" +packages = [{include = "rl_algo_impls"}] + +[tool.poetry.dependencies] +python = "~3.10" +"AutoROM.accept-rom-license" = "^0.4.2" +stable-baselines3 = {extras = ["extra"], version = "^1.7.0"} +scipy = "^1.10.0" +gym = {extras = ["box2d"], version = "^0.21.0"} +pyglet = "1.5.27" +PyYAML = "^6.0" +tensorboard = "^2.11.0" +pybullet = "^3.2.5" +wandb = "^0.13.9" +conda-lock = "^1.3.0" +torch-tb-profiler = "^0.4.1" +jupyter = "^1.0.0" +tabulate = "^0.9.0" +huggingface-hub = "^0.12.0" +cryptography = "39.0.1" +pyvirtualdisplay = "^3.0" +numexpr = "^2.8.4" +gym3 = "^0.3.3" +glfw = "1.12.0" +ipython = "^8.10.0" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" diff --git a/replay.meta.json b/replay.meta.json new file mode 100644 index 0000000000000000000000000000000000000000..b37a03424f5f020e7a2aa9e8777570b227d0cb9e --- /dev/null +++ b/replay.meta.json @@ -0,0 +1 @@ +{"content_type": "video/mp4", "encoder_version": {"backend": "ffmpeg", "version": "b'ffmpeg version 4.2.7-0ubuntu0.1 Copyright (c) 2000-2022 the FFmpeg developers\\nbuilt with gcc 9 (Ubuntu 9.4.0-1ubuntu1~20.04.1)\\nconfiguration: --prefix=/usr --extra-version=0ubuntu0.1 --toolchain=hardened --libdir=/usr/lib/x86_64-linux-gnu --incdir=/usr/include/x86_64-linux-gnu --arch=amd64 --enable-gpl --disable-stripping --enable-avresample --disable-filter=resample --enable-avisynth --enable-gnutls --enable-ladspa --enable-libaom --enable-libass --enable-libbluray --enable-libbs2b --enable-libcaca --enable-libcdio --enable-libcodec2 --enable-libflite --enable-libfontconfig --enable-libfreetype --enable-libfribidi --enable-libgme --enable-libgsm --enable-libjack --enable-libmp3lame --enable-libmysofa --enable-libopenjpeg --enable-libopenmpt --enable-libopus --enable-libpulse --enable-librsvg --enable-librubberband --enable-libshine --enable-libsnappy --enable-libsoxr --enable-libspeex --enable-libssh --enable-libtheora --enable-libtwolame --enable-libvidstab --enable-libvorbis --enable-libvpx --enable-libwavpack --enable-libwebp --enable-libx265 --enable-libxml2 --enable-libxvid --enable-libzmq --enable-libzvbi --enable-lv2 --enable-omx --enable-openal --enable-opencl --enable-opengl --enable-sdl2 --enable-libdc1394 --enable-libdrm --enable-libiec61883 --enable-nvenc --enable-chromaprint --enable-frei0r --enable-libx264 --enable-shared\\nlibavutil 56. 31.100 / 56. 31.100\\nlibavcodec 58. 54.100 / 58. 54.100\\nlibavformat 58. 29.100 / 58. 29.100\\nlibavdevice 58. 8.100 / 58. 8.100\\nlibavfilter 7. 57.100 / 7. 57.100\\nlibavresample 4. 0. 0 / 4. 0. 0\\nlibswscale 5. 5.100 / 5. 5.100\\nlibswresample 3. 5.100 / 3. 5.100\\nlibpostproc 55. 5.100 / 55. 5.100\\n'", "cmdline": ["ffmpeg", "-nostats", "-loglevel", "error", "-y", "-f", "rawvideo", "-s:v", "160x210", "-pix_fmt", "rgb24", "-framerate", "30", "-i", "-", "-vf", "scale=trunc(iw/2)*2:trunc(ih/2)*2", "-vcodec", "libx264", "-pix_fmt", "yuv420p", "-r", "30", "/tmp/tmp5g9x6mzt/a2c-PongNoFrameskip-v4/replay.mp4"]}, "episode": {"r": 21.0, "l": 6634, "t": 3.800911}} \ No newline at end of file diff --git a/replay.mp4 b/replay.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..0f0ee6d6c558879ba71a3a96a0c84b07d0d61c0c Binary files /dev/null and b/replay.mp4 differ diff --git a/runner/config.py b/runner/config.py new file mode 100644 index 0000000000000000000000000000000000000000..32045975fdcb7a7d604cac75f43eceb0e8dfedae --- /dev/null +++ b/runner/config.py @@ -0,0 +1,155 @@ +import os + +from datetime import datetime +from dataclasses import dataclass +from typing import Any, Dict, NamedTuple, Optional, TypedDict, Union + + +@dataclass +class RunArgs: + algo: str + env: str + seed: Optional[int] = None + use_deterministic_algorithms: bool = True + + +class EnvHyperparams(NamedTuple): + env_type: str = "gymvec" + n_envs: int = 1 + frame_stack: int = 1 + make_kwargs: Optional[Dict[str, Any]] = None + no_reward_timeout_steps: Optional[int] = None + no_reward_fire_steps: Optional[int] = None + vec_env_class: str = "sync" + normalize: bool = False + normalize_kwargs: Optional[Dict[str, Any]] = None + rolling_length: int = 100 + train_record_video: bool = False + video_step_interval: Union[int, float] = 1_000_000 + initial_steps_to_truncate: Optional[int] = None + clip_atari_rewards: bool = True + + +class Hyperparams(TypedDict, total=False): + device: str + n_timesteps: Union[int, float] + env_hyperparams: Dict[str, Any] + policy_hyperparams: Dict[str, Any] + algo_hyperparams: Dict[str, Any] + eval_params: Dict[str, Any] + + +@dataclass +class Config: + args: RunArgs + hyperparams: Hyperparams + root_dir: str + run_id: str = datetime.now().isoformat() + + def seed(self, training: bool = True) -> Optional[int]: + seed = self.args.seed + if training or seed is None: + return seed + return seed + self.env_hyperparams.get("n_envs", 1) + + @property + def device(self) -> str: + return self.hyperparams.get("device", "auto") + + @property + def n_timesteps(self) -> int: + return int(self.hyperparams.get("n_timesteps", 100_000)) + + @property + def env_hyperparams(self) -> Dict[str, Any]: + return self.hyperparams.get("env_hyperparams", {}) + + @property + def policy_hyperparams(self) -> Dict[str, Any]: + return self.hyperparams.get("policy_hyperparams", {}) + + @property + def algo_hyperparams(self) -> Dict[str, Any]: + return self.hyperparams.get("algo_hyperparams", {}) + + @property + def eval_params(self) -> Dict[str, Any]: + return self.hyperparams.get("eval_params", {}) + + @property + def algo(self) -> str: + return self.args.algo + + @property + def env_id(self) -> str: + return self.hyperparams.get("env_id") or self.args.env + + def model_name(self, include_seed: bool = True) -> str: + # Use arg env name instead of environment name + parts = [self.algo, self.args.env] + if include_seed and self.args.seed is not None: + parts.append(f"S{self.args.seed}") + + # Assume that the custom arg name already has the necessary information + if not self.hyperparams.get("env_id"): + make_kwargs = self.env_hyperparams.get("make_kwargs", {}) + if make_kwargs: + for k, v in make_kwargs.items(): + if type(v) == bool and v: + parts.append(k) + elif type(v) == int and v: + parts.append(f"{k}{v}") + else: + parts.append(str(v)) + + return "-".join(parts) + + @property + def run_name(self) -> str: + parts = [self.model_name(), self.run_id] + return "-".join(parts) + + @property + def saved_models_dir(self) -> str: + return os.path.join(self.root_dir, "saved_models") + + @property + def downloaded_models_dir(self) -> str: + return os.path.join(self.root_dir, "downloaded_models") + + def model_dir_name( + self, + best: bool = False, + extension: str = "", + ) -> str: + return self.model_name() + ("-best" if best else "") + extension + + def model_dir_path(self, best: bool = False, downloaded: bool = False) -> str: + return os.path.join( + self.saved_models_dir if not downloaded else self.downloaded_models_dir, + self.model_dir_name(best=best), + ) + + @property + def runs_dir(self) -> str: + return os.path.join(self.root_dir, "runs") + + @property + def tensorboard_summary_path(self) -> str: + return os.path.join(self.runs_dir, self.run_name) + + @property + def logs_path(self) -> str: + return os.path.join(self.runs_dir, f"log.yml") + + @property + def videos_dir(self) -> str: + return os.path.join(self.root_dir, "videos") + + @property + def video_prefix(self) -> str: + return os.path.join(self.videos_dir, self.model_name()) + + @property + def best_videos_dir(self) -> str: + return os.path.join(self.videos_dir, f"{self.model_name()}-best") diff --git a/runner/env.py b/runner/env.py new file mode 100644 index 0000000000000000000000000000000000000000..65082d31618fb80de07fc6b95ac5e0267144ddd0 --- /dev/null +++ b/runner/env.py @@ -0,0 +1,281 @@ +import gym +import numpy as np +import os + +from gym.vector.async_vector_env import AsyncVectorEnv +from gym.vector.sync_vector_env import SyncVectorEnv +from gym.wrappers.resize_observation import ResizeObservation +from gym.wrappers.gray_scale_observation import GrayScaleObservation +from gym.wrappers.frame_stack import FrameStack +from stable_baselines3.common.atari_wrappers import ( + MaxAndSkipEnv, + NoopResetEnv, +) +from stable_baselines3.common.vec_env.dummy_vec_env import DummyVecEnv +from stable_baselines3.common.vec_env.subproc_vec_env import SubprocVecEnv +from stable_baselines3.common.vec_env.vec_normalize import VecNormalize +from torch.utils.tensorboard.writer import SummaryWriter +from typing import Callable, Optional + +from runner.config import Config, EnvHyperparams +from shared.policy.policy import VEC_NORMALIZE_FILENAME +from wrappers.atari_wrappers import EpisodicLifeEnv, FireOnLifeStarttEnv, ClipRewardEnv +from wrappers.episode_record_video import EpisodeRecordVideo +from wrappers.episode_stats_writer import EpisodeStatsWriter +from wrappers.initial_step_truncate_wrapper import InitialStepTruncateWrapper +from wrappers.is_vector_env import IsVectorEnv +from wrappers.noop_env_seed import NoopEnvSeed +from wrappers.normalize import NormalizeObservation, NormalizeReward +from wrappers.transpose_image_observation import TransposeImageObservation +from wrappers.vectorable_wrapper import VecEnv +from wrappers.video_compat_wrapper import VideoCompatWrapper + + +def make_env( + config: Config, + hparams: EnvHyperparams, + training: bool = True, + render: bool = False, + normalize_load_path: Optional[str] = None, + tb_writer: Optional[SummaryWriter] = None, +) -> VecEnv: + if hparams.env_type == "procgen": + return _make_procgen_env( + config, + hparams, + training=training, + render=render, + normalize_load_path=normalize_load_path, + tb_writer=tb_writer, + ) + elif hparams.env_type in {"sb3vec", "gymvec"}: + return _make_vec_env( + config, + hparams, + training=training, + render=render, + normalize_load_path=normalize_load_path, + tb_writer=tb_writer, + ) + else: + raise ValueError(f"env_type {hparams.env_type} not supported") + + +def make_eval_env( + config: Config, + hparams: EnvHyperparams, + override_n_envs: Optional[int] = None, + **kwargs, +) -> VecEnv: + kwargs = kwargs.copy() + kwargs["training"] = False + if override_n_envs is not None: + hparams_kwargs = hparams._asdict() + hparams_kwargs["n_envs"] = override_n_envs + if override_n_envs == 1: + hparams_kwargs["vec_env_class"] = "sync" + hparams = EnvHyperparams(**hparams_kwargs) + return make_env(config, hparams, **kwargs) + + +def _make_vec_env( + config: Config, + hparams: EnvHyperparams, + training: bool = True, + render: bool = False, + normalize_load_path: Optional[str] = None, + tb_writer: Optional[SummaryWriter] = None, +) -> VecEnv: + ( + env_type, + n_envs, + frame_stack, + make_kwargs, + no_reward_timeout_steps, + no_reward_fire_steps, + vec_env_class, + normalize, + normalize_kwargs, + rolling_length, + train_record_video, + video_step_interval, + initial_steps_to_truncate, + clip_atari_rewards, + ) = hparams + + if "BulletEnv" in config.env_id: + import pybullet_envs + + spec = gym.spec(config.env_id) + seed = config.seed(training=training) + + make_kwargs = make_kwargs.copy() if make_kwargs is not None else {} + if "BulletEnv" in config.env_id and render: + make_kwargs["render"] = True + if "CarRacing" in config.env_id: + make_kwargs["verbose"] = 0 + if "procgen" in config.env_id: + if not render: + make_kwargs["render_mode"] = "rgb_array" + + def make(idx: int) -> Callable[[], gym.Env]: + def _make() -> gym.Env: + env = gym.make(config.env_id, **make_kwargs) + env = gym.wrappers.RecordEpisodeStatistics(env) + env = VideoCompatWrapper(env) + if training and train_record_video and idx == 0: + env = EpisodeRecordVideo( + env, + config.video_prefix, + step_increment=n_envs, + video_step_interval=int(video_step_interval), + ) + if training and initial_steps_to_truncate: + env = InitialStepTruncateWrapper( + env, idx * initial_steps_to_truncate // n_envs + ) + if "AtariEnv" in spec.entry_point: # type: ignore + env = NoopResetEnv(env, noop_max=30) + env = MaxAndSkipEnv(env, skip=4) + env = EpisodicLifeEnv(env, training=training) + action_meanings = env.unwrapped.get_action_meanings() + if "FIRE" in action_meanings: # type: ignore + env = FireOnLifeStarttEnv(env, action_meanings.index("FIRE")) + if clip_atari_rewards: + env = ClipRewardEnv(env, training=training) + env = ResizeObservation(env, (84, 84)) + env = GrayScaleObservation(env, keep_dim=False) + env = FrameStack(env, frame_stack) + elif "CarRacing" in config.env_id: + env = ResizeObservation(env, (64, 64)) + env = GrayScaleObservation(env, keep_dim=False) + env = FrameStack(env, frame_stack) + elif "procgen" in config.env_id: + # env = GrayScaleObservation(env, keep_dim=False) + env = NoopEnvSeed(env) + env = TransposeImageObservation(env) + if frame_stack > 1: + env = FrameStack(env, frame_stack) + + if no_reward_timeout_steps: + from wrappers.no_reward_timeout import NoRewardTimeout + + env = NoRewardTimeout( + env, no_reward_timeout_steps, n_fire_steps=no_reward_fire_steps + ) + + if seed is not None: + env.seed(seed + idx) + env.action_space.seed(seed + idx) + env.observation_space.seed(seed + idx) + + return env + + return _make + + if env_type == "sb3vec": + VecEnvClass = {"sync": DummyVecEnv, "async": SubprocVecEnv}[vec_env_class] + elif env_type == "gymvec": + VecEnvClass = {"sync": SyncVectorEnv, "async": AsyncVectorEnv}[vec_env_class] + else: + raise ValueError(f"env_type {env_type} unsupported") + envs = VecEnvClass([make(i) for i in range(n_envs)]) + if training: + assert tb_writer + envs = EpisodeStatsWriter( + envs, tb_writer, training=training, rolling_length=rolling_length + ) + if normalize: + normalize_kwargs = normalize_kwargs or {} + if env_type == "sb3vec": + if normalize_load_path: + envs = VecNormalize.load( + os.path.join(normalize_load_path, VEC_NORMALIZE_FILENAME), + envs, # type: ignore + ) + else: + envs = VecNormalize( + envs, # type: ignore + training=training, + **normalize_kwargs, + ) + if not training: + envs.norm_reward = False + else: + if normalize_kwargs.get("norm_obs", True): + envs = NormalizeObservation( + envs, training=training, clip=normalize_kwargs.get("clip_obs", 10.0) + ) + if training and normalize_kwargs.get("norm_reward", True): + envs = NormalizeReward( + envs, + training=training, + clip=normalize_kwargs.get("clip_reward", 10.0), + ) + return envs + + +def _make_procgen_env( + config: Config, + hparams: EnvHyperparams, + training: bool = True, + render: bool = False, + normalize_load_path: Optional[str] = None, + tb_writer: Optional[SummaryWriter] = None, +) -> VecEnv: + from gym3 import ViewerWrapper, ExtractDictObWrapper + from procgen.env import ProcgenGym3Env, ToBaselinesVecEnv + + ( + _, + n_envs, + frame_stack, + make_kwargs, + _, # no_reward_timeout_steps + _, # no_reward_fire_steps + _, # vec_env_class + normalize, + normalize_kwargs, + rolling_length, + _, # train_record_video + _, # video_step_interval + _, # initial_steps_to_truncate + _, # clip_atari_rewards + ) = hparams + + seed = config.seed(training=training) + + make_kwargs = make_kwargs or {} + make_kwargs["render_mode"] = "rgb_array" + if seed is not None: + make_kwargs["rand_seed"] = seed + + envs = ProcgenGym3Env(n_envs, config.env_id, **make_kwargs) + envs = ExtractDictObWrapper(envs, key="rgb") + if render: + envs = ViewerWrapper(envs, info_key="rgb") + envs = ToBaselinesVecEnv(envs) + envs = IsVectorEnv(envs) + # TODO: Handle Grayscale and/or FrameStack + envs = TransposeImageObservation(envs) + + envs = gym.wrappers.RecordEpisodeStatistics(envs) + + if seed is not None: + envs.action_space.seed(seed) + envs.observation_space.seed(seed) + + if training: + assert tb_writer + envs = EpisodeStatsWriter( + envs, tb_writer, training=training, rolling_length=rolling_length + ) + if normalize and training: + normalize_kwargs = normalize_kwargs or {} + envs = gym.wrappers.NormalizeReward(envs) + clip_obs = normalize_kwargs.get("clip_reward", 10.0) + envs = gym.wrappers.TransformReward( + envs, lambda r: np.clip(r, -clip_obs, clip_obs) + ) + + return envs # type: ignore diff --git a/runner/evaluate.py b/runner/evaluate.py new file mode 100644 index 0000000000000000000000000000000000000000..85893bacfbfdbcb7a1ba794b040d67fd6bff71b4 --- /dev/null +++ b/runner/evaluate.py @@ -0,0 +1,103 @@ +import os +import shutil + +from dataclasses import dataclass +from typing import NamedTuple, Optional + +from runner.env import make_eval_env +from runner.config import Config, EnvHyperparams, RunArgs +from runner.running_utils import ( + load_hyperparams, + set_seeds, + get_device, + make_policy, +) +from shared.callbacks.eval_callback import evaluate +from shared.policy.policy import Policy +from shared.stats import EpisodesStats + + +@dataclass +class EvalArgs(RunArgs): + render: bool = True + best: bool = True + n_envs: Optional[int] = 1 + n_episodes: int = 3 + deterministic_eval: Optional[bool] = None + no_print_returns: bool = False + wandb_run_path: Optional[str] = None + + +class Evaluation(NamedTuple): + policy: Policy + stats: EpisodesStats + config: Config + + +def evaluate_model(args: EvalArgs, root_dir: str) -> Evaluation: + if args.wandb_run_path: + import wandb + + api = wandb.Api() + run = api.run(args.wandb_run_path) + hyperparams = run.config + + args.algo = hyperparams["algo"] + args.env = hyperparams["env"] + args.seed = hyperparams.get("seed", None) + args.use_deterministic_algorithms = hyperparams.get( + "use_deterministic_algorithms", True + ) + + config = Config(args, hyperparams, root_dir) + model_path = config.model_dir_path(best=args.best, downloaded=True) + + model_archive_name = config.model_dir_name(best=args.best, extension=".zip") + run.file(model_archive_name).download() + if os.path.isdir(model_path): + shutil.rmtree(model_path) + shutil.unpack_archive(model_archive_name, model_path) + os.remove(model_archive_name) + else: + hyperparams = load_hyperparams(args.algo, args.env, root_dir) + + config = Config(args, hyperparams, root_dir) + model_path = config.model_dir_path(best=args.best) + + print(args) + + set_seeds(args.seed, args.use_deterministic_algorithms) + + env = make_eval_env( + config, + EnvHyperparams(**config.env_hyperparams), + override_n_envs=args.n_envs, + render=args.render, + normalize_load_path=model_path, + ) + device = get_device(config.device, env) + policy = make_policy( + args.algo, + env, + device, + load_path=model_path, + **config.policy_hyperparams, + ).eval() + + deterministic = ( + args.deterministic_eval + if args.deterministic_eval is not None + else config.eval_params.get("deterministic", True) + ) + return Evaluation( + policy, + evaluate( + env, + policy, + args.n_episodes, + render=args.render, + deterministic=deterministic, + print_returns=not args.no_print_returns, + ), + config, + ) diff --git a/runner/running_utils.py b/runner/running_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..cfbc25fb6c97606300fa6d9fbcd7e22138789515 --- /dev/null +++ b/runner/running_utils.py @@ -0,0 +1,195 @@ +import argparse +import gym +import json +import matplotlib.pyplot as plt +import numpy as np +import os +import random +import torch +import torch.backends.cudnn +import yaml + +from gym.spaces import Box, Discrete +from torch.utils.tensorboard.writer import SummaryWriter +from typing import Dict, Optional, Type, Union + +from runner.config import Hyperparams +from shared.algorithm import Algorithm +from shared.callbacks.eval_callback import EvalCallback +from shared.policy.on_policy import ActorCritic +from shared.policy.policy import Policy + +from a2c.a2c import A2C +from dqn.dqn import DQN +from dqn.policy import DQNPolicy +from ppo.ppo import PPO +from vpg.vpg import VanillaPolicyGradient +from vpg.policy import VPGActorCritic +from wrappers.vectorable_wrapper import VecEnv, single_observation_space + +ALGOS: Dict[str, Type[Algorithm]] = { + "dqn": DQN, + "vpg": VanillaPolicyGradient, + "ppo": PPO, + "a2c": A2C, +} +POLICIES: Dict[str, Type[Policy]] = { + "dqn": DQNPolicy, + "vpg": VPGActorCritic, + "ppo": ActorCritic, + "a2c": ActorCritic, +} + +HYPERPARAMS_PATH = "hyperparams" + + +def base_parser(multiple: bool = True) -> argparse.ArgumentParser: + parser = argparse.ArgumentParser() + parser.add_argument( + "--algo", + default=["dqn"], + type=str, + choices=list(ALGOS.keys()), + nargs="+" if multiple else 1, + help="Abbreviation(s) of algorithm(s)", + ) + parser.add_argument( + "--env", + default=["CartPole-v1"], + type=str, + nargs="+" if multiple else 1, + help="Name of environment(s) in gym", + ) + parser.add_argument( + "--seed", + default=[1], + type=int, + nargs="*" if multiple else "?", + help="Seeds to run experiment. Unset will do one run with no set seed", + ) + parser.add_argument( + "--use-deterministic-algorithms", + default=True, + type=bool, + help="If seed set, set torch.use_deterministic_algorithms", + ) + return parser + + +def load_hyperparams(algo: str, env_id: str, root_path: str) -> Hyperparams: + hyperparams_path = os.path.join(root_path, HYPERPARAMS_PATH, f"{algo}.yml") + with open(hyperparams_path, "r") as f: + hyperparams_dict = yaml.safe_load(f) + + if env_id in hyperparams_dict: + return hyperparams_dict[env_id] + + if "BulletEnv" in env_id: + import pybullet_envs + spec = gym.spec(env_id) + if "AtariEnv" in str(spec.entry_point) and "_atari" in hyperparams_dict: + return hyperparams_dict["_atari"] + else: + raise ValueError(f"{env_id} not specified in {algo} hyperparameters file") + + +def get_device(device: str, env: VecEnv) -> torch.device: + # cuda by default + if device == "auto": + device = "cuda" + # Apple MPS is a second choice (sometimes) + if device == "cuda" and not torch.cuda.is_available(): + device = "mps" + # If no MPS, fallback to cpu + if device == "mps" and not torch.backends.mps.is_available(): + device = "cpu" + # Simple environments like Discreet and 1-D Boxes might also be better + # served with the CPU. + if device == "mps": + obs_space = single_observation_space(env) + if isinstance(obs_space, Discrete): + device = "cpu" + elif isinstance(obs_space, Box) and len(obs_space.shape) == 1: + device = "cpu" + print(f"Device: {device}") + return torch.device(device) + + +def set_seeds(seed: Optional[int], use_deterministic_algorithms: bool) -> None: + if seed is None: + return + random.seed(seed) + np.random.seed(seed) + torch.manual_seed(seed) + torch.backends.cudnn.benchmark = False + torch.use_deterministic_algorithms(use_deterministic_algorithms) + os.environ["CUBLAS_WORKSPACE_CONFIG"] = ":4096:8" + # Stop warning and it would introduce stochasticity if I was using TF + os.environ["TF_ENABLE_ONEDNN_OPTS"] = "0" + + +def make_policy( + algo: str, + env: VecEnv, + device: torch.device, + load_path: Optional[str] = None, + **kwargs, +) -> Policy: + policy = POLICIES[algo](env, **kwargs).to(device) + if load_path: + policy.load(load_path) + return policy + + +def plot_eval_callback(callback: EvalCallback, tb_writer: SummaryWriter, run_name: str): + figure = plt.figure() + cumulative_steps = [ + (idx + 1) * callback.step_freq for idx in range(len(callback.stats)) + ] + plt.plot( + cumulative_steps, + [s.score.mean for s in callback.stats], + "b-", + label="mean", + ) + plt.plot( + cumulative_steps, + [s.score.mean - s.score.std for s in callback.stats], + "g--", + label="mean-std", + ) + plt.fill_between( + cumulative_steps, + [s.score.min for s in callback.stats], # type: ignore + [s.score.max for s in callback.stats], # type: ignore + facecolor="cyan", + label="range", + ) + plt.xlabel("Steps") + plt.ylabel("Score") + plt.legend() + plt.title(f"Eval {run_name}") + tb_writer.add_figure("eval", figure) + + +Scalar = Union[bool, str, float, int, None] + + +def hparam_dict( + hyperparams: Hyperparams, args: Dict[str, Union[Scalar, list]] +) -> Dict[str, Scalar]: + flattened = args.copy() + for k, v in flattened.items(): + if isinstance(v, list): + flattened[k] = json.dumps(v) + for k, v in hyperparams.items(): + if isinstance(v, dict): + for sk, sv in v.items(): + key = f"{k}/{sk}" + if isinstance(sv, dict) or isinstance(sv, list): + flattened[key] = str(sv) + else: + flattened[key] = sv + else: + flattened[k] = v # type: ignore + return flattened # type: ignore diff --git a/runner/train.py b/runner/train.py new file mode 100644 index 0000000000000000000000000000000000000000..ef6f7ac7773498d5c7dd9291cc4821715ec0f6b9 --- /dev/null +++ b/runner/train.py @@ -0,0 +1,141 @@ +# Support for PyTorch mps mode (https://pytorch.org/docs/stable/notes/mps.html) +import os + +os.environ["PYTORCH_ENABLE_MPS_FALLBACK"] = "1" + +import dataclasses +import shutil +import wandb +import yaml + +from dataclasses import dataclass +from torch.utils.tensorboard.writer import SummaryWriter +from typing import Any, Dict, Optional, Sequence + +from shared.callbacks.eval_callback import EvalCallback +from runner.config import Config, EnvHyperparams, RunArgs +from runner.env import make_env, make_eval_env +from runner.running_utils import ( + ALGOS, + load_hyperparams, + set_seeds, + get_device, + make_policy, + plot_eval_callback, + hparam_dict, +) +from shared.stats import EpisodesStats + + +@dataclass +class TrainArgs(RunArgs): + wandb_project_name: Optional[str] = None + wandb_entity: Optional[str] = None + wandb_tags: Sequence[str] = dataclasses.field(default_factory=list) + + +def train(args: TrainArgs): + print(args) + hyperparams = load_hyperparams(args.algo, args.env, os.getcwd()) + print(hyperparams) + config = Config(args, hyperparams, os.getcwd()) + + wandb_enabled = args.wandb_project_name + if wandb_enabled: + wandb.tensorboard.patch( + root_logdir=config.tensorboard_summary_path, pytorch=True + ) + wandb.init( + project=args.wandb_project_name, + entity=args.wandb_entity, + config=hyperparams, # type: ignore + name=config.run_name, + monitor_gym=True, + save_code=True, + tags=args.wandb_tags, + ) + wandb.config.update(args) + + tb_writer = SummaryWriter(config.tensorboard_summary_path) + + set_seeds(args.seed, args.use_deterministic_algorithms) + + env = make_env( + config, EnvHyperparams(**config.env_hyperparams), tb_writer=tb_writer + ) + device = get_device(config.device, env) + policy = make_policy(args.algo, env, device, **config.policy_hyperparams) + algo = ALGOS[args.algo](policy, env, device, tb_writer, **config.algo_hyperparams) + + num_parameters = policy.num_parameters() + num_trainable_parameters = policy.num_trainable_parameters() + if wandb_enabled: + wandb.run.summary["num_parameters"] = num_parameters + wandb.run.summary["num_trainable_parameters"] = num_trainable_parameters + else: + print( + f"num_parameters = {num_parameters} ; " + f"num_trainable_parameters = {num_trainable_parameters}" + ) + + eval_env = make_eval_env(config, EnvHyperparams(**config.env_hyperparams)) + record_best_videos = config.eval_params.get("record_best_videos", True) + callback = EvalCallback( + policy, + eval_env, + tb_writer, + best_model_path=config.model_dir_path(best=True), + **config.eval_params, + video_env=make_eval_env( + config, EnvHyperparams(**config.env_hyperparams), override_n_envs=1 + ) + if record_best_videos + else None, + best_video_dir=config.best_videos_dir, + ) + algo.learn(config.n_timesteps, callback=callback) + + policy.save(config.model_dir_path(best=False)) + + eval_stats = callback.evaluate(n_episodes=10, print_returns=True) + + plot_eval_callback(callback, tb_writer, config.run_name) + + log_dict: Dict[str, Any] = { + "eval": eval_stats._asdict(), + } + if callback.best: + log_dict["best_eval"] = callback.best._asdict() + log_dict.update(hyperparams) + log_dict.update(vars(args)) + with open(config.logs_path, "a") as f: + yaml.dump({config.run_name: log_dict}, f) + + best_eval_stats: EpisodesStats = callback.best # type: ignore + tb_writer.add_hparams( + hparam_dict(hyperparams, vars(args)), + { + "hparam/best_mean": best_eval_stats.score.mean, + "hparam/best_result": best_eval_stats.score.mean + - best_eval_stats.score.std, + "hparam/last_mean": eval_stats.score.mean, + "hparam/last_result": eval_stats.score.mean - eval_stats.score.std, + }, + None, + config.run_name, + ) + + tb_writer.close() + + if wandb_enabled: + shutil.make_archive( + os.path.join(wandb.run.dir, config.model_dir_name()), + "zip", + config.model_dir_path(), + ) + shutil.make_archive( + os.path.join(wandb.run.dir, config.model_dir_name(best=True)), + "zip", + config.model_dir_path(best=True), + ) + wandb.finish() diff --git a/saved_models/a2c-PongNoFrameskip-v4-S3-best/model.pth b/saved_models/a2c-PongNoFrameskip-v4-S3-best/model.pth new file mode 100644 index 0000000000000000000000000000000000000000..c7fcacff241bff4fab87f6dfc19f28f8152f77bd --- /dev/null +++ b/saved_models/a2c-PongNoFrameskip-v4-S3-best/model.pth @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6cf582760ef73a22097e5c9699d683b469f9fbe43983a52cbd2dd415dffe9522 +size 6755173 diff --git a/shared/algorithm.py b/shared/algorithm.py new file mode 100644 index 0000000000000000000000000000000000000000..2a6fd4c10a4ca5bb6fc67d9bb7cb22b2dd18c9bb --- /dev/null +++ b/shared/algorithm.py @@ -0,0 +1,35 @@ +import gym +import torch + +from abc import ABC, abstractmethod +from torch.utils.tensorboard.writer import SummaryWriter +from typing import List, Optional, TypeVar + +from shared.callbacks.callback import Callback +from shared.policy.policy import Policy +from wrappers.vectorable_wrapper import VecEnv + +AlgorithmSelf = TypeVar("AlgorithmSelf", bound="Algorithm") + + +class Algorithm(ABC): + @abstractmethod + def __init__( + self, + policy: Policy, + env: VecEnv, + device: torch.device, + tb_writer: SummaryWriter, + **kwargs, + ) -> None: + super().__init__() + self.policy = policy + self.env = env + self.device = device + self.tb_writer = tb_writer + + @abstractmethod + def learn( + self: AlgorithmSelf, total_timesteps: int, callback: Optional[Callback] = None + ) -> AlgorithmSelf: + ... diff --git a/shared/callbacks/callback.py b/shared/callbacks/callback.py new file mode 100644 index 0000000000000000000000000000000000000000..f53784b7070d680817f6b8d9fc1fd13085289b89 --- /dev/null +++ b/shared/callbacks/callback.py @@ -0,0 +1,12 @@ +from abc import ABC, abstractmethod + + +class Callback(ABC): + + def __init__(self) -> None: + super().__init__() + self.timesteps_elapsed = 0 + + def on_step(self, timesteps_elapsed: int = 1) -> bool: + self.timesteps_elapsed += timesteps_elapsed + return True \ No newline at end of file diff --git a/shared/callbacks/eval_callback.py b/shared/callbacks/eval_callback.py new file mode 100644 index 0000000000000000000000000000000000000000..475b8b40d1e12cab5eeceb30b764cdfc3bbe6c8e --- /dev/null +++ b/shared/callbacks/eval_callback.py @@ -0,0 +1,199 @@ +import itertools +import numpy as np +import os + +from time import perf_counter +from torch.utils.tensorboard.writer import SummaryWriter +from typing import List, Optional, Union + +from shared.callbacks.callback import Callback +from shared.policy.policy import Policy +from shared.stats import Episode, EpisodeAccumulator, EpisodesStats +from wrappers.vec_episode_recorder import VecEpisodeRecorder +from wrappers.vectorable_wrapper import VecEnv + + +class EvaluateAccumulator(EpisodeAccumulator): + def __init__( + self, + num_envs: int, + goal_episodes: int, + print_returns: bool = True, + ignore_first_episode: bool = False, + ): + super().__init__(num_envs) + self.completed_episodes_by_env_idx = [[] for _ in range(num_envs)] + self.goal_episodes_per_env = int(np.ceil(goal_episodes / num_envs)) + self.print_returns = print_returns + if ignore_first_episode: + first_done = set() + + def should_record_done(idx: int) -> bool: + has_done_first_episode = idx in first_done + first_done.add(idx) + return has_done_first_episode + + self.should_record_done = should_record_done + else: + self.should_record_done = lambda idx: True + + def on_done(self, ep_idx: int, episode: Episode) -> None: + if ( + self.should_record_done(ep_idx) + and len(self.completed_episodes_by_env_idx[ep_idx]) + >= self.goal_episodes_per_env + ): + return + self.completed_episodes_by_env_idx[ep_idx].append(episode) + if self.print_returns: + print( + f"Episode {len(self)} | " + f"Score {episode.score} | " + f"Length {episode.length}" + ) + + def __len__(self) -> int: + return sum(len(ce) for ce in self.completed_episodes_by_env_idx) + + @property + def episodes(self) -> List[Episode]: + return list(itertools.chain(*self.completed_episodes_by_env_idx)) + + def is_done(self) -> bool: + return all( + len(ce) == self.goal_episodes_per_env + for ce in self.completed_episodes_by_env_idx + ) + + +def evaluate( + env: VecEnv, + policy: Policy, + n_episodes: int, + render: bool = False, + deterministic: bool = True, + print_returns: bool = True, + ignore_first_episode: bool = False, +) -> EpisodesStats: + policy.eval() + episodes = EvaluateAccumulator( + env.num_envs, n_episodes, print_returns, ignore_first_episode + ) + + obs = env.reset() + while not episodes.is_done(): + act = policy.act(obs, deterministic=deterministic) + obs, rew, done, _ = env.step(act) + episodes.step(rew, done) + if render: + env.render() + stats = EpisodesStats(episodes.episodes) + if print_returns: + print(stats) + return stats + + +class EvalCallback(Callback): + def __init__( + self, + policy: Policy, + env: VecEnv, + tb_writer: SummaryWriter, + best_model_path: Optional[str] = None, + step_freq: Union[int, float] = 50_000, + n_episodes: int = 10, + save_best: bool = True, + deterministic: bool = True, + record_best_videos: bool = True, + video_env: Optional[VecEnv] = None, + best_video_dir: Optional[str] = None, + max_video_length: int = 3600, + ignore_first_episode: bool = False, + ) -> None: + super().__init__() + self.policy = policy + self.env = env + self.tb_writer = tb_writer + self.best_model_path = best_model_path + self.step_freq = int(step_freq) + self.n_episodes = n_episodes + self.save_best = save_best + self.deterministic = deterministic + self.stats: List[EpisodesStats] = [] + self.best = None + + self.record_best_videos = record_best_videos + assert video_env or not record_best_videos + self.video_env = video_env + assert best_video_dir or not record_best_videos + self.best_video_dir = best_video_dir + if best_video_dir: + os.makedirs(best_video_dir, exist_ok=True) + self.max_video_length = max_video_length + self.best_video_base_path = None + + self.ignore_first_episode = ignore_first_episode + + def on_step(self, timesteps_elapsed: int = 1) -> bool: + super().on_step(timesteps_elapsed) + if self.timesteps_elapsed // self.step_freq >= len(self.stats): + self.policy.sync_normalization(self.env) + self.evaluate() + return True + + def evaluate( + self, n_episodes: Optional[int] = None, print_returns: Optional[bool] = None + ) -> EpisodesStats: + start_time = perf_counter() + eval_stat = evaluate( + self.env, + self.policy, + n_episodes or self.n_episodes, + deterministic=self.deterministic, + print_returns=print_returns or False, + ignore_first_episode=self.ignore_first_episode, + ) + end_time = perf_counter() + self.tb_writer.add_scalar( + "eval/steps_per_second", + eval_stat.length.sum() / (end_time - start_time), + self.timesteps_elapsed, + ) + self.policy.train(True) + print(f"Eval Timesteps: {self.timesteps_elapsed} | {eval_stat}") + + self.stats.append(eval_stat) + + if not self.best or eval_stat >= self.best: + strictly_better = not self.best or eval_stat > self.best + self.best = eval_stat + if self.save_best: + assert self.best_model_path + self.policy.save(self.best_model_path) + print("Saved best model") + self.best.write_to_tensorboard( + self.tb_writer, "best_eval", self.timesteps_elapsed + ) + if strictly_better and self.record_best_videos: + assert self.video_env and self.best_video_dir + self.policy.sync_normalization(self.video_env) + self.best_video_base_path = os.path.join( + self.best_video_dir, str(self.timesteps_elapsed) + ) + video_wrapped = VecEpisodeRecorder( + self.video_env, + self.best_video_base_path, + max_video_length=self.max_video_length, + ) + video_stats = evaluate( + video_wrapped, + self.policy, + 1, + deterministic=self.deterministic, + print_returns=False, + ) + print(f"Saved best video: {video_stats}") + + eval_stat.write_to_tensorboard(self.tb_writer, "eval", self.timesteps_elapsed) + + return eval_stat diff --git a/shared/gae.py b/shared/gae.py new file mode 100644 index 0000000000000000000000000000000000000000..05d87467d365696fb5cdb184aa690cce0ef745c5 --- /dev/null +++ b/shared/gae.py @@ -0,0 +1,67 @@ +import numpy as np +import torch + +from typing import NamedTuple, Sequence + +from shared.policy.on_policy import OnPolicy +from shared.trajectory import Trajectory + + +class RtgAdvantage(NamedTuple): + rewards_to_go: torch.Tensor + advantage: torch.Tensor + + +def discounted_cumsum(x: np.ndarray, gamma: float) -> np.ndarray: + dc = x.copy() + for i in reversed(range(len(x) - 1)): + dc[i] += gamma * dc[i + 1] + return dc + + +def compute_advantage( + trajectories: Sequence[Trajectory], + policy: OnPolicy, + gamma: float, + gae_lambda: float, + device: torch.device, +) -> torch.Tensor: + advantage = [] + for traj in trajectories: + last_val = 0 + if not traj.terminated and traj.next_obs is not None: + last_val = policy.value(traj.next_obs) + rew = np.append(np.array(traj.rew), last_val) + v = np.append(np.array(traj.v), last_val) + deltas = rew[:-1] + gamma * v[1:] - v[:-1] + advantage.append(discounted_cumsum(deltas, gamma * gae_lambda)) + return torch.as_tensor( + np.concatenate(advantage), dtype=torch.float32, device=device + ) + + +def compute_rtg_and_advantage( + trajectories: Sequence[Trajectory], + policy: OnPolicy, + gamma: float, + gae_lambda: float, + device: torch.device, +) -> RtgAdvantage: + rewards_to_go = [] + advantages = [] + for traj in trajectories: + last_val = 0 + if not traj.terminated and traj.next_obs is not None: + last_val = policy.value(traj.next_obs) + rew = np.append(np.array(traj.rew), last_val) + v = np.append(np.array(traj.v), last_val) + deltas = rew[:-1] + gamma * v[1:] - v[:-1] + adv = discounted_cumsum(deltas, gamma * gae_lambda) + advantages.append(adv) + rewards_to_go.append(v[:-1] + adv) + return RtgAdvantage( + torch.as_tensor( + np.concatenate(rewards_to_go), dtype=torch.float32, device=device + ), + torch.as_tensor(np.concatenate(advantages), dtype=torch.float32, device=device), + ) diff --git a/shared/module/feature_extractor.py b/shared/module/feature_extractor.py new file mode 100644 index 0000000000000000000000000000000000000000..a3abeba287e9ff9da3408d0b362c6a11e878a060 --- /dev/null +++ b/shared/module/feature_extractor.py @@ -0,0 +1,215 @@ +import gym +import torch +import torch.nn as nn +import torch.nn.functional as F + +from abc import ABC, abstractmethod +from gym.spaces import Box, Discrete +from stable_baselines3.common.preprocessing import get_flattened_obs_dim +from typing import Dict, Optional, Sequence, Type + +from shared.module.module import layer_init + + +class CnnFeatureExtractor(nn.Module, ABC): + @abstractmethod + def __init__( + self, + in_channels: int, + activation: Type[nn.Module] = nn.ReLU, + init_layers_orthogonal: Optional[bool] = None, + **kwargs, + ) -> None: + super().__init__() + + +class NatureCnn(CnnFeatureExtractor): + """ + CNN from DQN Nature paper: Mnih, Volodymyr, et al. + "Human-level control through deep reinforcement learning." + Nature 518.7540 (2015): 529-533. + """ + + def __init__( + self, + in_channels: int, + activation: Type[nn.Module] = nn.ReLU, + init_layers_orthogonal: Optional[bool] = None, + **kwargs, + ) -> None: + if init_layers_orthogonal is None: + init_layers_orthogonal = True + super().__init__(in_channels, activation, init_layers_orthogonal) + self.cnn = nn.Sequential( + layer_init( + nn.Conv2d(in_channels, 32, kernel_size=8, stride=4), + init_layers_orthogonal, + ), + activation(), + layer_init( + nn.Conv2d(32, 64, kernel_size=4, stride=2), + init_layers_orthogonal, + ), + activation(), + layer_init( + nn.Conv2d(64, 64, kernel_size=3, stride=1), + init_layers_orthogonal, + ), + activation(), + nn.Flatten(), + ) + + def forward(self, obs: torch.Tensor) -> torch.Tensor: + return self.cnn(obs) + + +class ResidualBlock(nn.Module): + def __init__( + self, + channels: int, + activation: Type[nn.Module] = nn.ReLU, + init_layers_orthogonal: bool = False, + ) -> None: + super().__init__() + self.residual = nn.Sequential( + activation(), + layer_init( + nn.Conv2d(channels, channels, 3, padding=1), init_layers_orthogonal + ), + activation(), + layer_init( + nn.Conv2d(channels, channels, 3, padding=1), init_layers_orthogonal + ), + ) + + def forward(self, x: torch.Tensor) -> torch.Tensor: + return x + self.residual(x) + + +class ConvSequence(nn.Module): + def __init__( + self, + in_channels: int, + out_channels: int, + activation: Type[nn.Module] = nn.ReLU, + init_layers_orthogonal: bool = False, + ) -> None: + super().__init__() + self.seq = nn.Sequential( + layer_init( + nn.Conv2d(in_channels, out_channels, 3, padding=1), + init_layers_orthogonal, + ), + nn.MaxPool2d(3, stride=2, padding=1), + ResidualBlock(out_channels, activation, init_layers_orthogonal), + ResidualBlock(out_channels, activation, init_layers_orthogonal), + ) + + def forward(self, x: torch.Tensor) -> torch.Tensor: + return self.seq(x) + + +class ImpalaCnn(CnnFeatureExtractor): + """ + IMPALA-style CNN architecture + """ + + def __init__( + self, + in_channels: int, + activation: Type[nn.Module] = nn.ReLU, + init_layers_orthogonal: Optional[bool] = None, + impala_channels: Sequence[int] = (16, 32, 32), + **kwargs, + ) -> None: + if init_layers_orthogonal is None: + init_layers_orthogonal = False + super().__init__(in_channels, activation, init_layers_orthogonal) + sequences = [] + for out_channels in impala_channels: + sequences.append( + ConvSequence( + in_channels, out_channels, activation, init_layers_orthogonal + ) + ) + in_channels = out_channels + sequences.extend( + [ + activation(), + nn.Flatten(), + ] + ) + self.seq = nn.Sequential(*sequences) + + def forward(self, obs: torch.Tensor) -> torch.Tensor: + return self.seq(obs) + + +CNN_EXTRACTORS_BY_STYLE: Dict[str, Type[CnnFeatureExtractor]] = { + "nature": NatureCnn, + "impala": ImpalaCnn, +} + + +class FeatureExtractor(nn.Module): + def __init__( + self, + obs_space: gym.Space, + activation: Type[nn.Module], + init_layers_orthogonal: bool = False, + cnn_feature_dim: int = 512, + cnn_style: str = "nature", + cnn_layers_init_orthogonal: Optional[bool] = None, + impala_channels: Sequence[int] = (16, 32, 32), + ) -> None: + super().__init__() + if isinstance(obs_space, Box): + # Conv2D: (channels, height, width) + if len(obs_space.shape) == 3: + cnn = CNN_EXTRACTORS_BY_STYLE[cnn_style]( + obs_space.shape[0], + activation, + init_layers_orthogonal=cnn_layers_init_orthogonal, + impala_channels=impala_channels, + ) + + def preprocess(obs: torch.Tensor) -> torch.Tensor: + if len(obs.shape) == 3: + obs = obs.unsqueeze(0) + return obs.float() / 255.0 + + with torch.no_grad(): + cnn_out = cnn(preprocess(torch.as_tensor(obs_space.sample()))) + self.preprocess = preprocess + self.feature_extractor = nn.Sequential( + cnn, + layer_init( + nn.Linear(cnn_out.shape[1], cnn_feature_dim), + init_layers_orthogonal, + ), + activation(), + ) + self.out_dim = cnn_feature_dim + elif len(obs_space.shape) == 1: + + def preprocess(obs: torch.Tensor) -> torch.Tensor: + if len(obs.shape) == 1: + obs = obs.unsqueeze(0) + return obs.float() + + self.preprocess = preprocess + self.feature_extractor = nn.Flatten() + self.out_dim = get_flattened_obs_dim(obs_space) + else: + raise ValueError(f"Unsupported observation space: {obs_space}") + elif isinstance(obs_space, Discrete): + self.preprocess = lambda x: F.one_hot(x, obs_space.n).float() + self.feature_extractor = nn.Flatten() + self.out_dim = obs_space.n + else: + raise NotImplementedError + + def forward(self, obs: torch.Tensor) -> torch.Tensor: + if self.preprocess: + obs = self.preprocess(obs) + return self.feature_extractor(obs) diff --git a/shared/module/module.py b/shared/module/module.py new file mode 100644 index 0000000000000000000000000000000000000000..c579fb2a3808de47ec8d4c5233eea947b5cf0d28 --- /dev/null +++ b/shared/module/module.py @@ -0,0 +1,40 @@ +import numpy as np +import torch.nn as nn + +from typing import Sequence, Type + + +def mlp( + layer_sizes: Sequence[int], + activation: Type[nn.Module], + output_activation: Type[nn.Module] = nn.Identity, + init_layers_orthogonal: bool = False, + final_layer_gain: float = np.sqrt(2), +) -> nn.Module: + layers = [] + for i in range(len(layer_sizes) - 2): + layers.append( + layer_init( + nn.Linear(layer_sizes[i], layer_sizes[i + 1]), init_layers_orthogonal + ) + ) + layers.append(activation()) + layers.append( + layer_init( + nn.Linear(layer_sizes[-2], layer_sizes[-1]), + init_layers_orthogonal, + std=final_layer_gain, + ) + ) + layers.append(output_activation()) + return nn.Sequential(*layers) + + +def layer_init( + layer: nn.Module, init_layers_orthogonal: bool, std: float = np.sqrt(2) +) -> nn.Module: + if not init_layers_orthogonal: + return layer + nn.init.orthogonal_(layer.weight, std) # type: ignore + nn.init.constant_(layer.bias, 0.0) # type: ignore + return layer diff --git a/shared/policy/actor.py b/shared/policy/actor.py new file mode 100644 index 0000000000000000000000000000000000000000..202280cb85bae1fded982c65f86520197a20ae51 --- /dev/null +++ b/shared/policy/actor.py @@ -0,0 +1,305 @@ +import gym +import torch +import torch.nn as nn + +from abc import ABC, abstractmethod +from gym.spaces import Box, Discrete +from torch.distributions import Categorical, Distribution, Normal +from typing import NamedTuple, Optional, Sequence, Type, TypeVar, Union + +from shared.module.feature_extractor import FeatureExtractor +from shared.module.module import mlp + + +class PiForward(NamedTuple): + pi: Distribution + logp_a: Optional[torch.Tensor] + entropy: Optional[torch.Tensor] + + +class Actor(nn.Module, ABC): + @abstractmethod + def forward(self, obs: torch.Tensor, a: Optional[torch.Tensor] = None) -> PiForward: + ... + + +class CategoricalActorHead(Actor): + def __init__( + self, + act_dim: int, + hidden_sizes: Sequence[int] = (32,), + activation: Type[nn.Module] = nn.Tanh, + init_layers_orthogonal: bool = True, + ) -> None: + super().__init__() + layer_sizes = tuple(hidden_sizes) + (act_dim,) + self._fc = mlp( + layer_sizes, + activation, + init_layers_orthogonal=init_layers_orthogonal, + final_layer_gain=0.01, + ) + + def forward(self, obs: torch.Tensor, a: Optional[torch.Tensor] = None) -> PiForward: + logits = self._fc(obs) + pi = Categorical(logits=logits) + logp_a = None + entropy = None + if a is not None: + logp_a = pi.log_prob(a) + entropy = pi.entropy() + return PiForward(pi, logp_a, entropy) + + +class GaussianDistribution(Normal): + def log_prob(self, a: torch.Tensor) -> torch.Tensor: + return super().log_prob(a).sum(axis=-1) + + def sample(self) -> torch.Tensor: + return self.rsample() + + +class GaussianActorHead(Actor): + def __init__( + self, + act_dim: int, + hidden_sizes: Sequence[int] = (32,), + activation: Type[nn.Module] = nn.Tanh, + init_layers_orthogonal: bool = True, + log_std_init: float = -0.5, + ) -> None: + super().__init__() + layer_sizes = tuple(hidden_sizes) + (act_dim,) + self.mu_net = mlp( + layer_sizes, + activation, + init_layers_orthogonal=init_layers_orthogonal, + final_layer_gain=0.01, + ) + self.log_std = nn.Parameter( + torch.ones(act_dim, dtype=torch.float32) * log_std_init + ) + + def _distribution(self, obs: torch.Tensor) -> Distribution: + mu = self.mu_net(obs) + std = torch.exp(self.log_std) + return GaussianDistribution(mu, std) + + def forward(self, obs: torch.Tensor, a: Optional[torch.Tensor] = None) -> PiForward: + pi = self._distribution(obs) + logp_a = None + entropy = None + if a is not None: + logp_a = pi.log_prob(a) + entropy = pi.entropy() + return PiForward(pi, logp_a, entropy) + + +class TanhBijector: + def __init__(self, epsilon: float = 1e-6) -> None: + self.epsilon = epsilon + + @staticmethod + def forward(x: torch.Tensor) -> torch.Tensor: + return torch.tanh(x) + + @staticmethod + def inverse(y: torch.Tensor) -> torch.Tensor: + eps = torch.finfo(y.dtype).eps + clamped_y = y.clamp(min=-1.0 + eps, max=1.0 - eps) + return torch.atanh(clamped_y) + + def log_prob_correction(self, x: torch.Tensor) -> torch.Tensor: + return torch.log(1.0 - torch.tanh(x) ** 2 + self.epsilon) + + +class StateDependentNoiseDistribution(Normal): + def __init__( + self, + loc, + scale, + latent_sde: torch.Tensor, + exploration_mat: torch.Tensor, + exploration_matrices: torch.Tensor, + bijector: Optional[TanhBijector] = None, + validate_args=None, + ): + super().__init__(loc, scale, validate_args) + self.latent_sde = latent_sde + self.exploration_mat = exploration_mat + self.exploration_matrices = exploration_matrices + self.bijector = bijector + + def log_prob(self, a: torch.Tensor) -> torch.Tensor: + gaussian_a = self.bijector.inverse(a) if self.bijector else a + log_prob = super().log_prob(gaussian_a).sum(axis=-1) + if self.bijector: + log_prob -= torch.sum(self.bijector.log_prob_correction(gaussian_a), dim=1) + return log_prob + + def sample(self) -> torch.Tensor: + noise = self._get_noise() + actions = self.mean + noise + return self.bijector.forward(actions) if self.bijector else actions + + def _get_noise(self) -> torch.Tensor: + if len(self.latent_sde) == 1 or len(self.latent_sde) != len( + self.exploration_matrices + ): + return torch.mm(self.latent_sde, self.exploration_mat) + # (batch_size, n_features) -> (batch_size, 1, n_features) + latent_sde = self.latent_sde.unsqueeze(dim=1) + # (batch_size, 1, n_actions) + noise = torch.bmm(latent_sde, self.exploration_matrices) + return noise.squeeze(dim=1) + + @property + def mode(self) -> torch.Tensor: + mean = super().mode + return self.bijector.forward(mean) if self.bijector else mean + + +StateDependentNoiseActorHeadSelf = TypeVar( + "StateDependentNoiseActorHeadSelf", bound="StateDependentNoiseActorHead" +) + + +class StateDependentNoiseActorHead(Actor): + def __init__( + self, + act_dim: int, + hidden_sizes: Sequence[int] = (32,), + activation: Type[nn.Module] = nn.Tanh, + init_layers_orthogonal: bool = True, + log_std_init: float = -0.5, + full_std: bool = True, + squash_output: bool = False, + learn_std: bool = False, + ) -> None: + super().__init__() + self.act_dim = act_dim + layer_sizes = tuple(hidden_sizes) + (self.act_dim,) + if len(layer_sizes) == 2: + self.latent_net = nn.Identity() + elif len(layer_sizes) > 2: + self.latent_net = mlp( + layer_sizes[:-1], + activation, + output_activation=activation, + init_layers_orthogonal=init_layers_orthogonal, + ) + else: + raise ValueError("hidden_sizes must be of at least length 1") + self.mu_net = mlp( + layer_sizes[-2:], + activation, + init_layers_orthogonal=init_layers_orthogonal, + final_layer_gain=0.01, + ) + self.full_std = full_std + std_dim = (hidden_sizes[-1], act_dim if self.full_std else 1) + self.log_std = nn.Parameter( + torch.ones(std_dim, dtype=torch.float32) * log_std_init + ) + self.bijector = TanhBijector() if squash_output else None + self.learn_std = learn_std + self.device = None + + self.exploration_mat = None + self.exploration_matrices = None + self.sample_weights() + + def to( + self: StateDependentNoiseActorHeadSelf, + device: Optional[torch.device] = None, + dtype: Optional[Union[torch.dtype, str]] = None, + non_blocking: bool = False, + ) -> StateDependentNoiseActorHeadSelf: + super().to(device, dtype, non_blocking) + self.device = device + return self + + def _distribution(self, obs: torch.Tensor) -> Distribution: + latent = self.latent_net(obs) + mu = self.mu_net(latent) + latent_sde = latent if self.learn_std else latent.detach() + variance = torch.mm(latent_sde**2, self._get_std() ** 2) + assert self.exploration_mat is not None + assert self.exploration_matrices is not None + return StateDependentNoiseDistribution( + mu, + torch.sqrt(variance + 1e-6), + latent_sde, + self.exploration_mat, + self.exploration_matrices, + self.bijector, + ) + + def _get_std(self) -> torch.Tensor: + std = torch.exp(self.log_std) + if self.full_std: + return std + ones = torch.ones(self.log_std.shape[0], self.act_dim) + if self.device: + ones = ones.to(self.device) + return ones * std + + def forward(self, obs: torch.Tensor, a: Optional[torch.Tensor] = None) -> PiForward: + pi = self._distribution(obs) + logp_a = None + entropy = None + if a is not None: + logp_a = pi.log_prob(a) + entropy = -logp_a + return PiForward(pi, logp_a, entropy) + + def sample_weights(self, batch_size: int = 1) -> None: + std = self._get_std() + weights_dist = Normal(torch.zeros_like(std), std) + # Reparametrization trick to pass gradients + self.exploration_mat = weights_dist.rsample() + self.exploration_matrices = weights_dist.rsample(torch.Size((batch_size,))) + + +def actor_head( + action_space: gym.Space, + hidden_sizes: Sequence[int], + init_layers_orthogonal: bool, + activation: Type[nn.Module], + log_std_init: float = -0.5, + use_sde: bool = False, + full_std: bool = True, + squash_output: bool = False, +) -> Actor: + assert not use_sde or isinstance( + action_space, Box + ), "use_sde only valid if Box action_space" + assert not squash_output or use_sde, "squash_output only valid if use_sde" + if isinstance(action_space, Discrete): + return CategoricalActorHead( + action_space.n, + hidden_sizes=hidden_sizes, + activation=activation, + init_layers_orthogonal=init_layers_orthogonal, + ) + elif isinstance(action_space, Box): + if use_sde: + return StateDependentNoiseActorHead( + action_space.shape[0], + hidden_sizes=hidden_sizes, + activation=activation, + init_layers_orthogonal=init_layers_orthogonal, + log_std_init=log_std_init, + full_std=full_std, + squash_output=squash_output, + ) + else: + return GaussianActorHead( + action_space.shape[0], + hidden_sizes=hidden_sizes, + activation=activation, + init_layers_orthogonal=init_layers_orthogonal, + log_std_init=log_std_init, + ) + else: + raise ValueError(f"Unsupported action space: {action_space}") diff --git a/shared/policy/critic.py b/shared/policy/critic.py new file mode 100644 index 0000000000000000000000000000000000000000..70ed0e0778c6809f943aab4da70bec3b88fc5cf2 --- /dev/null +++ b/shared/policy/critic.py @@ -0,0 +1,28 @@ +import gym +import torch +import torch.nn as nn + +from typing import Sequence, Type +from shared.module.feature_extractor import FeatureExtractor +from shared.module.module import mlp + + +class CriticHead(nn.Module): + def __init__( + self, + hidden_sizes: Sequence[int] = (32,), + activation: Type[nn.Module] = nn.Tanh, + init_layers_orthogonal: bool = True, + ) -> None: + super().__init__() + layer_sizes = tuple(hidden_sizes) + (1,) + self._fc = mlp( + layer_sizes, + activation, + init_layers_orthogonal=init_layers_orthogonal, + final_layer_gain=1.0, + ) + + def forward(self, obs: torch.Tensor) -> torch.Tensor: + v = self._fc(obs) + return v.squeeze(-1) diff --git a/shared/policy/on_policy.py b/shared/policy/on_policy.py new file mode 100644 index 0000000000000000000000000000000000000000..7c6517232d4cf0672594e354c4d3949c86d7c936 --- /dev/null +++ b/shared/policy/on_policy.py @@ -0,0 +1,222 @@ +import gym +import numpy as np +import torch + +from abc import abstractmethod +from gym.spaces import Box, Discrete, Space +from typing import NamedTuple, Optional, Sequence, Tuple, TypeVar + +from shared.module.feature_extractor import FeatureExtractor +from shared.policy.actor import PiForward, StateDependentNoiseActorHead, actor_head +from shared.policy.critic import CriticHead +from shared.policy.policy import ACTIVATION, Policy +from wrappers.vectorable_wrapper import ( + VecEnv, + VecEnvObs, + single_observation_space, + single_action_space, +) + + +class Step(NamedTuple): + a: np.ndarray + v: np.ndarray + logp_a: np.ndarray + clamped_a: np.ndarray + + +class ACForward(NamedTuple): + logp_a: torch.Tensor + entropy: torch.Tensor + v: torch.Tensor + + +FEAT_EXT_FILE_NAME = "feat_ext.pt" +V_FEAT_EXT_FILE_NAME = "v_feat_ext.pt" +PI_FILE_NAME = "pi.pt" +V_FILE_NAME = "v.pt" +ActorCriticSelf = TypeVar("ActorCriticSelf", bound="ActorCritic") + + +def clamp_actions( + actions: np.ndarray, action_space: gym.Space, squash_output: bool +) -> np.ndarray: + if isinstance(action_space, Box): + low, high = action_space.low, action_space.high # type: ignore + if squash_output: + # Squashed output is already between -1 and 1. Rescale if the actual + # output needs to something other than -1 and 1 + return low + 0.5 * (actions + 1) * (high - low) + else: + return np.clip(actions, low, high) + return actions + + +def default_hidden_sizes(obs_space: Space) -> Sequence[int]: + if isinstance(obs_space, Box): + if len(obs_space.shape) == 3: + # By default feature extractor to output has no hidden layers + return [] + elif len(obs_space.shape) == 1: + return [64, 64] + else: + raise ValueError(f"Unsupported observation space: {obs_space}") + elif isinstance(obs_space, Discrete): + return [64] + else: + raise ValueError(f"Unsupported observation space: {obs_space}") + + +class OnPolicy(Policy): + @abstractmethod + def value(self, obs: VecEnvObs) -> np.ndarray: + ... + + @abstractmethod + def step(self, obs: VecEnvObs) -> Step: + ... + + +class ActorCritic(OnPolicy): + def __init__( + self, + env: VecEnv, + pi_hidden_sizes: Optional[Sequence[int]] = None, + v_hidden_sizes: Optional[Sequence[int]] = None, + init_layers_orthogonal: bool = True, + activation_fn: str = "tanh", + log_std_init: float = -0.5, + use_sde: bool = False, + full_std: bool = True, + squash_output: bool = False, + share_features_extractor: bool = True, + cnn_feature_dim: int = 512, + cnn_style: str = "nature", + cnn_layers_init_orthogonal: Optional[bool] = None, + impala_channels: Sequence[int] = (16, 32, 32), + **kwargs, + ) -> None: + super().__init__(env, **kwargs) + + observation_space = single_observation_space(env) + action_space = single_action_space(env) + + pi_hidden_sizes = ( + pi_hidden_sizes + if pi_hidden_sizes is not None + else default_hidden_sizes(observation_space) + ) + v_hidden_sizes = ( + v_hidden_sizes + if v_hidden_sizes is not None + else default_hidden_sizes(observation_space) + ) + + activation = ACTIVATION[activation_fn] + self.action_space = action_space + self.squash_output = squash_output + self.share_features_extractor = share_features_extractor + self._feature_extractor = FeatureExtractor( + observation_space, + activation, + init_layers_orthogonal=init_layers_orthogonal, + cnn_feature_dim=cnn_feature_dim, + cnn_style=cnn_style, + cnn_layers_init_orthogonal=cnn_layers_init_orthogonal, + impala_channels=impala_channels, + ) + self._pi = actor_head( + self.action_space, + (self._feature_extractor.out_dim,) + tuple(pi_hidden_sizes), + init_layers_orthogonal, + activation, + log_std_init=log_std_init, + use_sde=use_sde, + full_std=full_std, + squash_output=squash_output, + ) + + if not share_features_extractor: + self._v_feature_extractor = FeatureExtractor( + observation_space, + activation, + init_layers_orthogonal=init_layers_orthogonal, + cnn_feature_dim=cnn_feature_dim, + cnn_style=cnn_style, + cnn_layers_init_orthogonal=cnn_layers_init_orthogonal, + ) + v_hidden_sizes = (self._v_feature_extractor.out_dim,) + tuple( + v_hidden_sizes + ) + else: + self._v_feature_extractor = None + v_hidden_sizes = (self._feature_extractor.out_dim,) + tuple(v_hidden_sizes) + self._v = CriticHead( + hidden_sizes=v_hidden_sizes, + activation=activation, + init_layers_orthogonal=init_layers_orthogonal, + ) + + def _pi_forward( + self, obs: torch.Tensor, action: Optional[torch.Tensor] = None + ) -> Tuple[PiForward, torch.Tensor]: + p_fe = self._feature_extractor(obs) + pi_forward = self._pi(p_fe, action) + + return pi_forward, p_fe + + def _v_forward(self, obs: torch.Tensor, p_fc: torch.Tensor) -> torch.Tensor: + v_fe = self._v_feature_extractor(obs) if self._v_feature_extractor else p_fc + return self._v(v_fe) + + def forward(self, obs: torch.Tensor, action: torch.Tensor) -> ACForward: + (_, logp_a, entropy), p_fc = self._pi_forward(obs, action) + v = self._v_forward(obs, p_fc) + + assert logp_a is not None + assert entropy is not None + return ACForward(logp_a, entropy, v) + + def value(self, obs: VecEnvObs) -> np.ndarray: + o = self._as_tensor(obs) + with torch.no_grad(): + fe = ( + self._v_feature_extractor(o) + if self._v_feature_extractor + else self._feature_extractor(o) + ) + v = self._v(fe) + return v.cpu().numpy() + + def step(self, obs: VecEnvObs) -> Step: + o = self._as_tensor(obs) + with torch.no_grad(): + (pi, _, _), p_fc = self._pi_forward(o) + a = pi.sample() + logp_a = pi.log_prob(a) + + v = self._v_forward(o, p_fc) + + a_np = a.cpu().numpy() + clamped_a_np = clamp_actions(a_np, self.action_space, self.squash_output) + return Step(a_np, v.cpu().numpy(), logp_a.cpu().numpy(), clamped_a_np) + + def act(self, obs: np.ndarray, deterministic: bool = True) -> np.ndarray: + if not deterministic: + return self.step(obs).clamped_a + else: + o = self._as_tensor(obs) + with torch.no_grad(): + (pi, _, _), _ = self._pi_forward(o) + a = pi.mode + return clamp_actions(a.cpu().numpy(), self.action_space, self.squash_output) + + def load(self, path: str) -> None: + super().load(path) + self.reset_noise() + + def reset_noise(self, batch_size: Optional[int] = None) -> None: + if isinstance(self._pi, StateDependentNoiseActorHead): + self._pi.sample_weights( + batch_size=batch_size if batch_size else self.env.num_envs + ) diff --git a/shared/policy/policy.py b/shared/policy/policy.py new file mode 100644 index 0000000000000000000000000000000000000000..f0669a5c21094b9e815b7348cdb7b20606f69601 --- /dev/null +++ b/shared/policy/policy.py @@ -0,0 +1,114 @@ +import numpy as np +import os +import torch +import torch.nn as nn + +from abc import ABC, abstractmethod +from copy import deepcopy +from stable_baselines3.common.vec_env import unwrap_vec_normalize +from stable_baselines3.common.vec_env.vec_normalize import VecNormalize +from typing import Dict, Optional, Type, TypeVar, Union + +from wrappers.normalize import NormalizeObservation, NormalizeReward +from wrappers.vectorable_wrapper import VecEnv, VecEnvObs, find_wrapper + +ACTIVATION: Dict[str, Type[nn.Module]] = { + "tanh": nn.Tanh, + "relu": nn.ReLU, +} + +VEC_NORMALIZE_FILENAME = "vecnormalize.pkl" +MODEL_FILENAME = "model.pth" +NORMALIZE_OBSERVATION_FILENAME = "norm_obs.npz" +NORMALIZE_REWARD_FILENAME = "norm_reward.npz" + +PolicySelf = TypeVar("PolicySelf", bound="Policy") + + +class Policy(nn.Module, ABC): + @abstractmethod + def __init__(self, env: VecEnv, **kwargs) -> None: + super().__init__() + self.env = env + self.vec_normalize = unwrap_vec_normalize(env) + self.norm_observation = find_wrapper(env, NormalizeObservation) + self.norm_reward = find_wrapper(env, NormalizeReward) + self.device = None + + def to( + self: PolicySelf, + device: Optional[torch.device] = None, + dtype: Optional[Union[torch.dtype, str]] = None, + non_blocking: bool = False, + ) -> PolicySelf: + super().to(device, dtype, non_blocking) + self.device = device + return self + + @abstractmethod + def act(self, obs: VecEnvObs, deterministic: bool = True) -> np.ndarray: + ... + + def save(self, path: str) -> None: + os.makedirs(path, exist_ok=True) + + if self.vec_normalize: + self.vec_normalize.save(os.path.join(path, VEC_NORMALIZE_FILENAME)) + if self.norm_observation: + self.norm_observation.save( + os.path.join(path, NORMALIZE_OBSERVATION_FILENAME) + ) + if self.norm_reward: + self.norm_reward.save(os.path.join(path, NORMALIZE_REWARD_FILENAME)) + torch.save( + self.state_dict(), + os.path.join(path, MODEL_FILENAME), + ) + + def load(self, path: str) -> None: + # VecNormalize load occurs in env.py + self.load_state_dict( + torch.load(os.path.join(path, MODEL_FILENAME), map_location=self.device) + ) + if self.norm_observation: + self.norm_observation.load( + os.path.join(path, NORMALIZE_OBSERVATION_FILENAME) + ) + if self.norm_reward: + self.norm_reward.load(os.path.join(path, NORMALIZE_REWARD_FILENAME)) + + def reset_noise(self) -> None: + pass + + def _as_tensor(self, obs: VecEnvObs) -> torch.Tensor: + assert isinstance(obs, np.ndarray) + o = torch.as_tensor(obs) + if self.device is not None: + o = o.to(self.device) + return o + + def num_trainable_parameters(self) -> int: + return sum(p.numel() for p in self.parameters() if p.requires_grad) + + def num_parameters(self) -> int: + return sum(p.numel() for p in self.parameters()) + + def sync_normalization(self, destination_env) -> None: + current = destination_env + while current != current.unwrapped: + if isinstance(current, VecNormalize): + assert self.vec_normalize + current.ret_rms = deepcopy(self.vec_normalize.ret_rms) + if hasattr(self.vec_normalize, "obs_rms"): + current.obs_rms = deepcopy(self.vec_normalize.obs_rms) + elif isinstance(current, NormalizeObservation): + assert self.norm_observation + current.rms = deepcopy(self.norm_observation.rms) + elif isinstance(current, NormalizeReward): + assert self.norm_reward + current.rms = deepcopy(self.norm_reward.rms) + current = getattr(current, "venv", getattr(current, "env", current)) + if not current: + raise AttributeError( + f"{type(current)} doesn't include env or venv attribute" + ) diff --git a/shared/schedule.py b/shared/schedule.py new file mode 100644 index 0000000000000000000000000000000000000000..1461a782341eff5d89a53f16aebdc268bf9f7f52 --- /dev/null +++ b/shared/schedule.py @@ -0,0 +1,31 @@ +from torch.optim import Optimizer +from typing import Callable + +Schedule = Callable[[float], float] + + +def linear_schedule( + start_val: float, end_val: float, end_fraction: float = 1.0 +) -> Schedule: + def func(progress_fraction: float) -> float: + if progress_fraction >= end_fraction: + return end_val + else: + return start_val + (end_val - start_val) * progress_fraction / end_fraction + + return func + + +def constant_schedule(val: float) -> Schedule: + return lambda f: val + + +def schedule(name: str, start_val: float) -> Schedule: + if name == "linear": + return linear_schedule(start_val, 0) + return constant_schedule(start_val) + + +def update_learning_rate(optimizer: Optimizer, learning_rate: float) -> None: + for param_group in optimizer.param_groups: + param_group["lr"] = learning_rate diff --git a/shared/stats.py b/shared/stats.py new file mode 100644 index 0000000000000000000000000000000000000000..2315e6bb0de04ee56ca577cb7444f17e93e88fc0 --- /dev/null +++ b/shared/stats.py @@ -0,0 +1,160 @@ +import numpy as np + +from dataclasses import dataclass +from torch.utils.tensorboard.writer import SummaryWriter +from typing import Dict, List, Optional, Sequence, Union, TypeVar + + +@dataclass +class Episode: + score: float = 0 + length: int = 0 + + +StatisticSelf = TypeVar("StatisticSelf", bound="Statistic") + + +@dataclass +class Statistic: + values: np.ndarray + round_digits: int = 2 + + @property + def mean(self) -> float: + return np.mean(self.values).item() + + @property + def std(self) -> float: + return np.std(self.values).item() + + @property + def min(self) -> float: + return np.min(self.values).item() + + @property + def max(self) -> float: + return np.max(self.values).item() + + def sum(self) -> float: + return np.sum(self.values).item() + + def __len__(self) -> int: + return len(self.values) + + def _diff(self: StatisticSelf, o: StatisticSelf) -> float: + return (self.mean - self.std) - (o.mean - o.std) + + def __gt__(self: StatisticSelf, o: StatisticSelf) -> bool: + return self._diff(o) > 0 + + def __ge__(self: StatisticSelf, o: StatisticSelf) -> bool: + return self._diff(o) >= 0 + + def __repr__(self) -> str: + mean = round(self.mean, self.round_digits) + std = round(self.std, self.round_digits) + if self.round_digits == 0: + mean = int(mean) + std = int(std) + return f"{mean} +/- {std}" + + def to_dict(self) -> Dict[str, float]: + return { + "mean": self.mean, + "std": self.std, + "min": self.min, + "max": self.max, + } + + +EpisodesStatsSelf = TypeVar("EpisodesStatsSelf", bound="EpisodesStats") + + +class EpisodesStats: + episodes: Sequence[Episode] + simple: bool + score: Statistic + length: Statistic + + def __init__(self, episodes: Sequence[Episode], simple: bool = False) -> None: + self.episodes = episodes + self.simple = simple + self.score = Statistic(np.array([e.score for e in episodes])) + self.length = Statistic(np.array([e.length for e in episodes]), round_digits=0) + + def __gt__(self: EpisodesStatsSelf, o: EpisodesStatsSelf) -> bool: + return self.score > o.score + + def __ge__(self: EpisodesStatsSelf, o: EpisodesStatsSelf) -> bool: + return self.score >= o.score + + def __repr__(self) -> str: + return ( + f"Score: {self.score} ({round(self.score.mean - self.score.std, 2)}) | " + f"Length: {self.length}" + ) + + def __len__(self) -> int: + return len(self.episodes) + + def _asdict(self) -> dict: + return { + "n_episodes": len(self.episodes), + "score": self.score.to_dict(), + "length": self.length.to_dict(), + } + + def write_to_tensorboard( + self, tb_writer: SummaryWriter, main_tag: str, global_step: Optional[int] = None + ) -> None: + stats = {"mean": self.score.mean} + if not self.simple: + stats.update( + { + "min": self.score.min, + "max": self.score.max, + "result": self.score.mean - self.score.std, + "n_episodes": len(self.episodes), + "length": self.length.mean, + } + ) + for name, value in stats.items(): + tb_writer.add_scalar(f"{main_tag}/{name}", value, global_step=global_step) + + +class EpisodeAccumulator: + def __init__(self, num_envs: int): + self._episodes = [] + self.current_episodes = [Episode() for _ in range(num_envs)] + + @property + def episodes(self) -> List[Episode]: + return self._episodes + + def step(self, reward: np.ndarray, done: np.ndarray) -> None: + for idx, current in enumerate(self.current_episodes): + current.score += reward[idx] + current.length += 1 + if done[idx]: + self._episodes.append(current) + self.current_episodes[idx] = Episode() + self.on_done(idx, current) + + def __len__(self) -> int: + return len(self.episodes) + + def on_done(self, ep_idx: int, episode: Episode) -> None: + pass + + def stats(self) -> EpisodesStats: + return EpisodesStats(self.episodes) + + +def log_scalars( + tb_writer: SummaryWriter, + main_tag: str, + tag_scalar_dict: Dict[str, Union[int, float]], + global_step: int, +) -> None: + for tag, value in tag_scalar_dict.items(): + tb_writer.add_scalar(f"{main_tag}/{tag}", value, global_step) diff --git a/shared/trajectory.py b/shared/trajectory.py new file mode 100644 index 0000000000000000000000000000000000000000..def6fc8e6876dafe777eb23d9d2e57ba93db5b45 --- /dev/null +++ b/shared/trajectory.py @@ -0,0 +1,81 @@ +import numpy as np + +from dataclasses import dataclass, field +from typing import Generic, List, Optional, Type, TypeVar + +from wrappers.vectorable_wrapper import VecEnvObs + + +@dataclass +class Trajectory: + obs: List[np.ndarray] = field(default_factory=list) + act: List[np.ndarray] = field(default_factory=list) + next_obs: Optional[np.ndarray] = None + rew: List[float] = field(default_factory=list) + terminated: bool = False + v: List[float] = field(default_factory=list) + + def add( + self, + obs: np.ndarray, + act: np.ndarray, + next_obs: np.ndarray, + rew: float, + terminated: bool, + v: float, + ): + self.obs.append(obs) + self.act.append(act) + self.next_obs = next_obs if not terminated else None + self.rew.append(rew) + self.terminated = terminated + self.v.append(v) + + def __len__(self) -> int: + return len(self.obs) + + +T = TypeVar("T", bound=Trajectory) + + +class TrajectoryAccumulator(Generic[T]): + def __init__(self, num_envs: int, trajectory_class: Type[T] = Trajectory) -> None: + self.num_envs = num_envs + self.trajectory_class = trajectory_class + + self._trajectories = [] + self._current_trajectories = [trajectory_class() for _ in range(num_envs)] + + def step( + self, + obs: VecEnvObs, + action: np.ndarray, + next_obs: VecEnvObs, + reward: np.ndarray, + done: np.ndarray, + val: np.ndarray, + *args, + ) -> None: + assert isinstance(obs, np.ndarray) + assert isinstance(next_obs, np.ndarray) + for i, args in enumerate(zip(obs, action, next_obs, reward, done, val, *args)): + trajectory = self._current_trajectories[i] + # TODO: Eventually take advantage of terminated/truncated differentiation in + # later versions of gym. + trajectory.add(*args) + if done[i]: + self._trajectories.append(trajectory) + self._current_trajectories[i] = self.trajectory_class() + self.on_done(i, trajectory) + + @property + def all_trajectories(self) -> List[T]: + return self._trajectories + list( + filter(lambda t: len(t), self._current_trajectories) + ) + + def n_timesteps(self) -> int: + return sum(len(t) for t in self.all_trajectories) + + def on_done(self, env_idx: int, trajectory: T) -> None: + pass diff --git a/train.py b/train.py new file mode 100644 index 0000000000000000000000000000000000000000..9df2a92d28e19ff9e2c7bab9742d05d3b4533638 --- /dev/null +++ b/train.py @@ -0,0 +1,80 @@ +# Support for PyTorch mps mode (https://pytorch.org/docs/stable/notes/mps.html) +import os + +os.environ["PYTORCH_ENABLE_MPS_FALLBACK"] = "1" + +import itertools + +from argparse import Namespace +from multiprocessing import Pool +from typing import Any, Dict + +from runner.running_utils import base_parser +from runner.train import train, TrainArgs + + +def args_dict(algo: str, env: str, seed: str, args: Namespace) -> Dict[str, Any]: + d = vars(args).copy() + d.update( + { + "algo": algo, + "env": env, + "seed": seed, + } + ) + return d + + +if __name__ == "__main__": + parser = base_parser() + parser.add_argument( + "--wandb-project-name", + type=str, + default="rl-algo-impls", + help="WandB project namme to upload training data to. If none, won't upload.", + ) + parser.add_argument( + "--wandb-entity", + type=str, + default=None, + help="WandB team of project. None uses default entity", + ) + parser.add_argument( + "--wandb-tags", type=str, nargs="*", help="WandB tags to add to run" + ) + parser.add_argument( + "--pool-size", type=int, default=1, help="Simultaneous training jobs to run" + ) + parser.set_defaults( + algo="ppo", + env="MountainCarContinuous-v0", + seed=[1, 2, 3], + pool_size=3, + ) + args = parser.parse_args() + print(args) + + if args.pool_size == 1: + from pyvirtualdisplay.display import Display + + virtual_display = Display(visible=False, size=(1400, 900)) + virtual_display.start() + + # pool_size isn't a TrainArg so must be removed from args + pool_size = min(args.pool_size, len(args.seed)) + delattr(args, "pool_size") + + algos = args.algo if isinstance(args.algo, list) else [args.algo] + envs = args.env if isinstance(args.env, list) else [args.env] + seeds = args.seed if isinstance(args.seed, list) else [args.seed] + if all(len(arg) == 1 for arg in [algos, envs, seeds]): + train(TrainArgs(**args_dict(algos[0], envs[0], seeds[0], args))) + else: + # Force a new process for each job to get around wandb not allowing more than one + # wandb.tensorboard.patch call per process. + with Pool(pool_size, maxtasksperchild=1) as p: + train_args = [ + TrainArgs(**args_dict(algo, env, seed, args)) + for algo, env, seed in itertools.product(algos, envs, seeds) + ] + p.map(train, train_args) diff --git a/vpg/policy.py b/vpg/policy.py new file mode 100644 index 0000000000000000000000000000000000000000..2e2553fbf0ab40f28509bb4d9d99084e2201aa96 --- /dev/null +++ b/vpg/policy.py @@ -0,0 +1,128 @@ +import numpy as np +import torch +import torch.nn as nn + +from typing import Optional, Sequence + +from shared.module.feature_extractor import FeatureExtractor +from shared.policy.actor import ( + PiForward, + Actor, + StateDependentNoiseActorHead, + actor_head, +) +from shared.policy.critic import CriticHead +from shared.policy.on_policy import ( + Step, + ACForward, + OnPolicy, + clamp_actions, + default_hidden_sizes, +) +from shared.policy.policy import ACTIVATION +from wrappers.vectorable_wrapper import VecEnv, VecEnvObs, single_observation_space, single_action_space + +PI_FILE_NAME = "pi.pt" +V_FILE_NAME = "v.pt" + + +class VPGActor(Actor): + def __init__(self, feature_extractor: FeatureExtractor, head: Actor) -> None: + super().__init__() + self.feature_extractor = feature_extractor + self.head = head + + def forward(self, obs: torch.Tensor, a: Optional[torch.Tensor] = None) -> PiForward: + fe = self.feature_extractor(obs) + return self.head(fe, a) + + +class VPGActorCritic(OnPolicy): + def __init__( + self, + env: VecEnv, + hidden_sizes: Optional[Sequence[int]] = None, + init_layers_orthogonal: bool = True, + activation_fn: str = "tanh", + log_std_init: float = -0.5, + use_sde: bool = False, + full_std: bool = True, + squash_output: bool = False, + **kwargs, + ) -> None: + super().__init__(env, **kwargs) + activation = ACTIVATION[activation_fn] + obs_space = single_observation_space(env) + self.action_space = single_action_space(env) + self.use_sde = use_sde + self.squash_output = squash_output + + hidden_sizes = ( + hidden_sizes + if hidden_sizes is not None + else default_hidden_sizes(obs_space) + ) + + pi_feature_extractor = FeatureExtractor( + obs_space, activation, init_layers_orthogonal=init_layers_orthogonal + ) + pi_head = actor_head( + self.action_space, + (pi_feature_extractor.out_dim,) + tuple(hidden_sizes), + init_layers_orthogonal, + activation, + log_std_init=log_std_init, + use_sde=use_sde, + full_std=full_std, + squash_output=squash_output, + ) + self.pi = VPGActor(pi_feature_extractor, pi_head) + + v_feature_extractor = FeatureExtractor( + obs_space, activation, init_layers_orthogonal=init_layers_orthogonal + ) + v_head = CriticHead( + (v_feature_extractor.out_dim,) + tuple(hidden_sizes), + activation=activation, + init_layers_orthogonal=init_layers_orthogonal, + ) + self.v = nn.Sequential(v_feature_extractor, v_head) + + def value(self, obs: VecEnvObs) -> np.ndarray: + o = self._as_tensor(obs) + with torch.no_grad(): + v = self.v(o) + return v.cpu().numpy() + + def step(self, obs: VecEnvObs) -> Step: + o = self._as_tensor(obs) + with torch.no_grad(): + pi, _, _ = self.pi(o) + a = pi.sample() + logp_a = pi.log_prob(a) + + v = self.v(o) + + a_np = a.cpu().numpy() + clamped_a_np = clamp_actions(a_np, self.action_space, self.squash_output) + return Step(a_np, v.cpu().numpy(), logp_a.cpu().numpy(), clamped_a_np) + + def act(self, obs: np.ndarray, deterministic: bool = True) -> np.ndarray: + if not deterministic: + return self.step(obs).clamped_a + else: + o = self._as_tensor(obs) + with torch.no_grad(): + pi, _, _ = self.pi(o) + a = pi.mode + return clamp_actions(a.cpu().numpy(), self.action_space, self.squash_output) + + def load(self, path: str) -> None: + super().load(path) + self.reset_noise() + + def reset_noise(self, batch_size: Optional[int] = None) -> None: + if isinstance(self.pi.head, StateDependentNoiseActorHead): + self.pi.head.sample_weights( + batch_size=batch_size if batch_size else self.env.num_envs + ) diff --git a/vpg/vpg.py b/vpg/vpg.py new file mode 100644 index 0000000000000000000000000000000000000000..6f47ebe532f37270eab10e7c388bc78754d01bc3 --- /dev/null +++ b/vpg/vpg.py @@ -0,0 +1,168 @@ +import numpy as np +import torch +import torch.nn as nn + +from collections import defaultdict +from dataclasses import dataclass, asdict +from torch.optim import Adam +from torch.utils.tensorboard.writer import SummaryWriter +from typing import Optional, Sequence, TypeVar + +from shared.algorithm import Algorithm +from shared.callbacks.callback import Callback +from shared.gae import compute_rtg_and_advantage, compute_advantage +from shared.trajectory import Trajectory, TrajectoryAccumulator +from vpg.policy import VPGActorCritic +from wrappers.vectorable_wrapper import VecEnv + + +@dataclass +class TrainEpochStats: + pi_loss: float + entropy_loss: float + v_loss: float + envs_with_done: int = 0 + episodes_done: int = 0 + + def write_to_tensorboard(self, tb_writer: SummaryWriter, global_step: int) -> None: + for name, value in asdict(self).items(): + tb_writer.add_scalar(f"losses/{name}", value, global_step=global_step) + + +class VPGTrajectoryAccumulator(TrajectoryAccumulator): + def __init__(self, num_envs: int) -> None: + super().__init__(num_envs, trajectory_class=Trajectory) + self.completed_per_env: defaultdict[int, int] = defaultdict(int) + + def on_done(self, env_idx: int, trajectory: Trajectory) -> None: + self.completed_per_env[env_idx] += 1 + + +VanillaPolicyGradientSelf = TypeVar( + "VanillaPolicyGradientSelf", bound="VanillaPolicyGradient" +) + + +class VanillaPolicyGradient(Algorithm): + def __init__( + self, + policy: VPGActorCritic, + env: VecEnv, + device: torch.device, + tb_writer: SummaryWriter, + gamma: float = 0.99, + pi_lr: float = 3e-4, + val_lr: float = 1e-3, + train_v_iters: int = 80, + gae_lambda: float = 0.97, + max_grad_norm: float = 10.0, + n_steps: int = 4_000, + sde_sample_freq: int = -1, + update_rtg_between_v_iters: bool = False, + ent_coef: float = 0.0, + ) -> None: + super().__init__(policy, env, device, tb_writer) + self.policy = policy + + self.gamma = gamma + self.gae_lambda = gae_lambda + self.pi_optim = Adam(self.policy.pi.parameters(), lr=pi_lr) + self.val_optim = Adam(self.policy.v.parameters(), lr=val_lr) + self.max_grad_norm = max_grad_norm + + self.n_steps = n_steps + self.train_v_iters = train_v_iters + self.sde_sample_freq = sde_sample_freq + self.update_rtg_between_v_iters = update_rtg_between_v_iters + + self.ent_coef = ent_coef + + def learn( + self: VanillaPolicyGradientSelf, + total_timesteps: int, + callback: Optional[Callback] = None, + ) -> VanillaPolicyGradientSelf: + timesteps_elapsed = 0 + epoch_cnt = 0 + while timesteps_elapsed < total_timesteps: + epoch_cnt += 1 + accumulator = self._collect_trajectories() + epoch_stats = self.train(accumulator.all_trajectories) + epoch_stats.envs_with_done = len(accumulator.completed_per_env) + epoch_stats.episodes_done = sum(accumulator.completed_per_env.values()) + epoch_steps = accumulator.n_timesteps() + timesteps_elapsed += epoch_steps + epoch_stats.write_to_tensorboard( + self.tb_writer, global_step=timesteps_elapsed + ) + print( + " | ".join( + [ + f"Epoch: {epoch_cnt}", + f"Pi Loss: {round(epoch_stats.pi_loss, 2)}", + f"Epoch Loss: {round(epoch_stats.entropy_loss, 2)}", + f"V Loss: {round(epoch_stats.v_loss, 2)}", + f"Total Steps: {timesteps_elapsed}", + ] + ) + ) + if callback: + callback.on_step(timesteps_elapsed=epoch_steps) + return self + + def train(self, trajectories: Sequence[Trajectory]) -> TrainEpochStats: + self.policy.train() + obs = torch.as_tensor( + np.concatenate([np.array(t.obs) for t in trajectories]), device=self.device + ) + act = torch.as_tensor( + np.concatenate([np.array(t.act) for t in trajectories]), device=self.device + ) + rtg, adv = compute_rtg_and_advantage( + trajectories, self.policy, self.gamma, self.gae_lambda, self.device + ) + + _, logp, entropy = self.policy.pi(obs, act) + pi_loss = -(logp * adv).mean() + entropy_loss = entropy.mean() + + actor_loss = pi_loss - self.ent_coef * entropy_loss + + self.pi_optim.zero_grad() + actor_loss.backward() + nn.utils.clip_grad_norm_(self.policy.pi.parameters(), self.max_grad_norm) + self.pi_optim.step() + + v_loss = 0 + for _ in range(self.train_v_iters): + if self.update_rtg_between_v_iters: + rtg = compute_advantage( + trajectories, self.policy, self.gamma, self.gae_lambda, self.device + ) + v = self.policy.v(obs) + v_loss = ((v - rtg) ** 2).mean() + + self.val_optim.zero_grad() + v_loss.backward() + nn.utils.clip_grad_norm_(self.policy.v.parameters(), self.max_grad_norm) + self.val_optim.step() + + return TrainEpochStats( + pi_loss.item(), + entropy_loss.item(), + v_loss.item(), # type: ignore + ) + + def _collect_trajectories(self) -> VPGTrajectoryAccumulator: + self.policy.eval() + obs = self.env.reset() + accumulator = VPGTrajectoryAccumulator(self.env.num_envs) + self.policy.reset_noise() + for i in range(self.n_steps): + if self.sde_sample_freq > 0 and i > 0 and i % self.sde_sample_freq == 0: + self.policy.reset_noise() + action, value, _, clamped_action = self.policy.step(obs) + next_obs, reward, done, _ = self.env.step(clamped_action) + accumulator.step(obs, action, next_obs, reward, done, value) + obs = next_obs + return accumulator diff --git a/wrappers/atari_wrappers.py b/wrappers/atari_wrappers.py new file mode 100644 index 0000000000000000000000000000000000000000..6b866eb9c0a80d7428de3468b8bb33d1a3c4d01a --- /dev/null +++ b/wrappers/atari_wrappers.py @@ -0,0 +1,84 @@ +import gym +import numpy as np + +from typing import Any, Dict, Tuple, Union + +from wrappers.vectorable_wrapper import VecotarableWrapper + +ObsType = Union[np.ndarray, dict] +ActType = Union[int, float, np.ndarray, dict] + + +class EpisodicLifeEnv(VecotarableWrapper): + def __init__(self, env: gym.Env, training: bool = True, noop_act: int = 0) -> None: + super().__init__(env) + self.training = training + self.noop_act = noop_act + self.life_done_continue = False + self.lives = 0 + + def step(self, action: ActType) -> Tuple[ObsType, float, bool, Dict[str, Any]]: + obs, rew, done, info = self.env.step(action) + new_lives = self.env.unwrapped.ale.lives() + self.life_done_continue = new_lives != self.lives and not done + # Only if training should life-end be marked as done + if self.training and 0 < new_lives < self.lives: + done = True + self.lives = new_lives + return obs, rew, done, info + + def reset(self, **kwargs) -> ObsType: + # If life_done_continue (but not game over), then a reset should just allow the + # game to progress to the next life. + if self.training and self.life_done_continue: + obs, _, _, _ = self.env.step(self.noop_act) + else: + obs = self.env.reset(**kwargs) + self.lives = self.env.unwrapped.ale.lives() + return obs + + +class FireOnLifeStarttEnv(VecotarableWrapper): + def __init__(self, env: gym.Env, fire_act: int = 1) -> None: + super().__init__(env) + self.fire_act = fire_act + action_meanings = env.unwrapped.get_action_meanings() + assert action_meanings[fire_act] == "FIRE" + assert len(action_meanings) >= 3 + self.lives = 0 + self.fire_on_next_step = True + + def step(self, action: ActType) -> Tuple[ObsType, float, bool, Dict[str, Any]]: + if self.fire_on_next_step: + action = self.fire_act + self.fire_on_next_step = False + obs, rew, done, info = self.env.step(action) + new_lives = self.env.unwrapped.ale.lives() + if 0 < new_lives < self.lives and not done: + self.fire_on_next_step = True + self.lives = new_lives + return obs, rew, done, info + + def reset(self, **kwargs) -> ObsType: + self.env.reset(**kwargs) + obs, _, done, _ = self.env.step(self.fire_act) + if done: + self.env.reset(**kwargs) + obs, _, done, _ = self.env.step(2) + if done: + self.env.reset(**kwargs) + self.fire_on_next_step = False + return obs + + +class ClipRewardEnv(VecotarableWrapper): + def __init__(self, env: gym.Env, training: bool = True) -> None: + super().__init__(env) + self.training = training + + def step(self, action: ActType) -> Tuple[ObsType, float, bool, Dict[str, Any]]: + obs, rew, done, info = self.env.step(action) + if self.training: + info["unclipped_reward"] = rew + rew = np.sign(rew) + return obs, rew, done, info diff --git a/wrappers/episode_record_video.py b/wrappers/episode_record_video.py new file mode 100644 index 0000000000000000000000000000000000000000..13aa32813b9318a549de799980a851a168821254 --- /dev/null +++ b/wrappers/episode_record_video.py @@ -0,0 +1,75 @@ +import gym +import numpy as np + +from gym.wrappers.monitoring.video_recorder import VideoRecorder +from typing import Tuple, Union + +from wrappers.vectorable_wrapper import VecotarableWrapper + +ObsType = Union[np.ndarray, dict] +ActType = Union[int, float, np.ndarray, dict] + + +class EpisodeRecordVideo(VecotarableWrapper): + def __init__( + self, + env: gym.Env, + video_path_prefix: str, + step_increment: int = 1, + video_step_interval: int = 1_000_000, + max_video_length: int = 3600, + ) -> None: + super().__init__(env) + self.video_path_prefix = video_path_prefix + self.step_increment = step_increment + self.video_step_interval = video_step_interval + self.max_video_length = max_video_length + self.total_steps = 0 + self.next_record_video_step = 0 + self.video_recorder = None + self.recorded_frames = 0 + + def step(self, action: ActType) -> Tuple[ObsType, float, bool, dict]: + obs, rew, done, info = self.env.step(action) + self.total_steps += self.step_increment + # Using first env to record episodes + if self.video_recorder: + self.video_recorder.capture_frame() + self.recorded_frames += 1 + if info.get("episode"): + episode_info = { + k: v.item() if hasattr(v, "item") else v + for k, v in info["episode"].items() + } + self.video_recorder.metadata["episode"] = episode_info + if self.recorded_frames > self.max_video_length: + self._close_video_recorder() + return obs, rew, done, info + + def reset(self, **kwargs) -> ObsType: + obs = self.env.reset(**kwargs) + if self.video_recorder: + self._close_video_recorder() + elif self.total_steps >= self.next_record_video_step: + self._start_video_recorder() + return obs + + def _start_video_recorder(self) -> None: + self._close_video_recorder() + + video_path = f"{self.video_path_prefix}-{self.next_record_video_step}" + self.video_recorder = VideoRecorder( + self.env, + base_path=video_path, + metadata={"step": self.total_steps}, + ) + + self.video_recorder.capture_frame() + self.recorded_frames = 1 + self.next_record_video_step += self.video_step_interval + + def _close_video_recorder(self) -> None: + if self.video_recorder: + self.video_recorder.close() + self.video_recorder = None + self.recorded_frames = 0 diff --git a/wrappers/episode_stats_writer.py b/wrappers/episode_stats_writer.py new file mode 100644 index 0000000000000000000000000000000000000000..05bcaf8a697097c987d36e11035da65b37b93a40 --- /dev/null +++ b/wrappers/episode_stats_writer.py @@ -0,0 +1,66 @@ +import numpy as np + +from collections import deque +from torch.utils.tensorboard.writer import SummaryWriter +from typing import Any, Dict, List + +from shared.stats import Episode, EpisodesStats +from wrappers.vectorable_wrapper import VecotarableWrapper, VecEnvStepReturn, VecEnvObs + + +class EpisodeStatsWriter(VecotarableWrapper): + def __init__( + self, + env, + tb_writer: SummaryWriter, + training: bool = True, + rolling_length=100, + ): + super().__init__(env) + self.training = training + self.tb_writer = tb_writer + self.rolling_length = rolling_length + self.episodes = deque(maxlen=rolling_length) + self.total_steps = 0 + self.episode_cnt = 0 + self.last_episode_cnt_print = 0 + + def step(self, actions: np.ndarray) -> VecEnvStepReturn: + obs, rews, dones, infos = self.env.step(actions) + self._record_stats(infos) + return obs, rews, dones, infos + + # Support for stable_baselines3.common.vec_env.VecEnvWrapper + def step_wait(self) -> VecEnvStepReturn: + obs, rews, dones, infos = self.env.step_wait() + self._record_stats(infos) + return obs, rews, dones, infos + + def _record_stats(self, infos: List[Dict[str, Any]]) -> None: + self.total_steps += getattr(self.env, "num_envs", 1) + step_episodes = [] + for info in infos: + ep_info = info.get("episode") + if ep_info: + episode = Episode(ep_info["r"], ep_info["l"]) + step_episodes.append(episode) + self.episodes.append(episode) + if step_episodes: + tag = "train" if self.training else "eval" + step_stats = EpisodesStats(step_episodes, simple=True) + step_stats.write_to_tensorboard(self.tb_writer, tag, self.total_steps) + rolling_stats = EpisodesStats(self.episodes) + rolling_stats.write_to_tensorboard( + self.tb_writer, f"{tag}_rolling", self.total_steps + ) + self.episode_cnt += len(step_episodes) + if self.episode_cnt >= self.last_episode_cnt_print + self.rolling_length: + print( + f"Episode: {self.episode_cnt} | " + f"Steps: {self.total_steps} | " + f"{rolling_stats}" + ) + self.last_episode_cnt_print += self.rolling_length + + def reset(self) -> VecEnvObs: + return self.env.reset() diff --git a/wrappers/initial_step_truncate_wrapper.py b/wrappers/initial_step_truncate_wrapper.py new file mode 100644 index 0000000000000000000000000000000000000000..ffb82afdeb7b2da98ee3386284016917b8be588b --- /dev/null +++ b/wrappers/initial_step_truncate_wrapper.py @@ -0,0 +1,27 @@ +import gym +import numpy as np + +from typing import Any, Dict, Tuple, Union + +from wrappers.vectorable_wrapper import VecotarableWrapper + +ObsType = Union[np.ndarray, dict] +ActType = Union[int, float, np.ndarray, dict] + + +class InitialStepTruncateWrapper(VecotarableWrapper): + def __init__(self, env: gym.Env, initial_steps_to_truncate: int) -> None: + super().__init__(env) + self.initial_steps_to_truncate = initial_steps_to_truncate + self.initialized = initial_steps_to_truncate == 0 + self.steps = 0 + + def step(self, action: ActType) -> Tuple[ObsType, float, bool, Dict[str, Any]]: + obs, rew, done, info = self.env.step(action) + if not self.initialized: + self.steps += 1 + if self.steps >= self.initial_steps_to_truncate: + print(f"Truncation at {self.steps} steps") + done = True + self.initialized = True + return obs, rew, done, info diff --git a/wrappers/is_vector_env.py b/wrappers/is_vector_env.py new file mode 100644 index 0000000000000000000000000000000000000000..668890fee7b02686f270b5c27ee2100f4331b827 --- /dev/null +++ b/wrappers/is_vector_env.py @@ -0,0 +1,13 @@ +from typing import Any + +from wrappers.vectorable_wrapper import VecotarableWrapper + + +class IsVectorEnv(VecotarableWrapper): + """ + Override to set properties to match gym.vector.VectorEnv + """ + + def __init__(self, env: Any) -> None: + super().__init__(env) + self.is_vector_env = True diff --git a/wrappers/no_reward_timeout.py b/wrappers/no_reward_timeout.py new file mode 100644 index 0000000000000000000000000000000000000000..fbefe131a83a7b58dfa8fa404095aba2ec5513c1 --- /dev/null +++ b/wrappers/no_reward_timeout.py @@ -0,0 +1,65 @@ +import gym +import numpy as np + +from typing import Optional, Tuple, Union + +from wrappers.vectorable_wrapper import VecotarableWrapper + +ObsType = Union[np.ndarray, dict] +ActType = Union[int, float, np.ndarray, dict] + + +class NoRewardTimeout(VecotarableWrapper): + def __init__( + self, env: gym.Env, n_timeout_steps: int, n_fire_steps: Optional[int] = None + ) -> None: + super().__init__(env) + self.n_timeout_steps = n_timeout_steps + self.n_fire_steps = n_fire_steps + + self.fire_act = None + if n_fire_steps is not None: + action_meanings = env.unwrapped.get_action_meanings() + assert "FIRE" in action_meanings + self.fire_act = action_meanings.index("FIRE") + + self.steps_since_reward = 0 + + self.episode_score = 0 + self.episode_step_idx = 0 + + def step(self, action: ActType) -> Tuple[ObsType, float, bool, dict]: + if self.steps_since_reward == self.n_fire_steps: + assert self.fire_act is not None + self.print_intervention("Force fire action") + action = self.fire_act + obs, rew, done, info = self.env.step(action) + + self.episode_score += rew + self.episode_step_idx += 1 + + if rew != 0 or done: + self.steps_since_reward = 0 + else: + self.steps_since_reward += 1 + if self.steps_since_reward >= self.n_timeout_steps: + self.print_intervention("Early terminate") + done = True + + return obs, rew, done, info + + def reset(self, **kwargs) -> ObsType: + self._reset_state() + return self.env.reset(**kwargs) + + def _reset_state(self) -> None: + self.steps_since_reward = 0 + self.episode_score = 0 + self.episode_step_idx = 0 + + def print_intervention(self, tag: str) -> None: + print( + f"{self.__class__.__name__}: {tag} | " + f"Score: {self.episode_score} | " + f"Length: {self.episode_step_idx}" + ) diff --git a/wrappers/noop_env_seed.py b/wrappers/noop_env_seed.py new file mode 100644 index 0000000000000000000000000000000000000000..027b56d35be750d0d765e6af94ce4fd3ead0544f --- /dev/null +++ b/wrappers/noop_env_seed.py @@ -0,0 +1,11 @@ +from typing import List, Optional + +from wrappers.vectorable_wrapper import VecotarableWrapper + +class NoopEnvSeed(VecotarableWrapper): + """ + Wrapper to stop a seed call going to the underlying environment. + """ + + def seed(self, seed: Optional[int] = None) -> Optional[List[int]]: + return None diff --git a/wrappers/normalize.py b/wrappers/normalize.py new file mode 100644 index 0000000000000000000000000000000000000000..550762b7ab729f41ae0cb8c84f949d3ddf4d56dd --- /dev/null +++ b/wrappers/normalize.py @@ -0,0 +1,140 @@ +import gym +import numpy as np + +from numpy.typing import NDArray +from typing import Tuple + +from wrappers.vectorable_wrapper import ( + VecotarableWrapper, + single_observation_space, +) + + +class RunningMeanStd: + def __init__(self, episilon: float = 1e-4, shape: Tuple[int, ...]=()) -> None: + self.mean = np.zeros(shape, np.float64) + self.var = np.ones(shape, np.float64) + self.count = episilon + + def update(self, x: NDArray) -> None: + batch_mean = np.mean(x, axis=0) + batch_var = np.var(x, axis=0) + batch_count = x.shape[0] + + delta = batch_mean - self.mean + total_count = self.count + batch_count + + self.mean += delta * batch_count / total_count + + m_a = self.var * self.count + m_b = batch_var * batch_count + M2 = m_a + m_b + np.square(delta) * self.count * batch_count / total_count + self.var = M2 / total_count + self.count = total_count + + +class NormalizeObservation(VecotarableWrapper): + def __init__( + self, + env: gym.Env, + training: bool = True, + epsilon: float = 1e-8, + clip: float = 10.0, + ) -> None: + super().__init__(env) + self.rms = RunningMeanStd(shape=single_observation_space(env).shape) + self.training = training + self.epsilon = epsilon + self.clip = clip + + def step(self, action): + obs, reward, done, info = self.env.step(action) + return self.normalize(obs), reward, done, info + + def reset(self, **kwargs): + obs = self.env.reset(**kwargs) + return self.normalize(obs) + + def normalize(self, obs: NDArray) -> NDArray: + obs_array = np.array([obs]) if not self.is_vector_env else obs + if self.training: + self.rms.update(obs_array) + normalized = np.clip( + (obs_array - self.rms.mean) / np.sqrt(self.rms.var + self.epsilon), + -self.clip, + self.clip, + ) + return normalized[0] if not self.is_vector_env else normalized + + def save(self, path: str) -> None: + np.savez_compressed( + path, + mean=self.rms.mean, + var=self.rms.var, + count=self.rms.count, + ) + + def load(self, path: str) -> None: + data = np.load(path) + self.rms.mean = data["mean"] + self.rms.var = data["var"] + self.rms.count = data["count"] + + +class NormalizeReward(VecotarableWrapper): + def __init__( + self, + env: gym.Env, + training: bool = True, + gamma: float = 0.99, + epsilon: float = 1e-8, + clip: float = 10.0, + ) -> None: + super().__init__(env) + self.rms = RunningMeanStd(shape=()) + self.training = training + self.gamma = gamma + self.epsilon = epsilon + self.clip = clip + + self.returns = np.zeros(self.num_envs) + + def step(self, action): + obs, reward, done, info = self.env.step(action) + + if not self.is_vector_env: + reward = np.array([reward]) + reward = self.normalize(reward) + if not self.is_vector_env: + reward = reward[0] + + dones = done if self.is_vector_env else np.array([done]) + self.returns[dones] = 0 + + return obs, reward, done, info + + def reset(self, **kwargs): + self.returns = np.zeros(self.num_envs) + return self.env.reset(**kwargs) + + def normalize(self, rewards): + if self.training: + self.returns = self.returns * self.gamma + rewards + self.rms.update(self.returns) + return np.clip( + rewards / np.sqrt(self.rms.var + self.epsilon), -self.clip, self.clip + ) + + def save(self, path: str) -> None: + np.savez_compressed( + path, + mean=self.rms.mean, + var=self.rms.var, + count=self.rms.count, + ) + + def load(self, path: str) -> None: + data = np.load(path) + self.rms.mean = data["mean"] + self.rms.var = data["var"] + self.rms.count = data["count"] diff --git a/wrappers/transpose_image_observation.py b/wrappers/transpose_image_observation.py new file mode 100644 index 0000000000000000000000000000000000000000..7076c9146fa28cd266a2466b58ac6a6b6555d59b --- /dev/null +++ b/wrappers/transpose_image_observation.py @@ -0,0 +1,34 @@ +import gym +import numpy as np + +from gym import ObservationWrapper +from gym.spaces import Box + + +class TransposeImageObservation(ObservationWrapper): + def __init__(self, env: gym.Env) -> None: + super().__init__(env) + + assert isinstance(env.observation_space, Box) + + obs_space = env.observation_space + axes = tuple(i for i in range(len(obs_space.shape))) + self._transpose_axes = axes[:-3] + (axes[-1],) + axes[-3:-1] + + self.observation_space = Box( + low=np.transpose(obs_space.low, axes=self._transpose_axes), + high=np.transpose(obs_space.high, axes=self._transpose_axes), + shape=[obs_space.shape[idx] for idx in self._transpose_axes], + dtype=obs_space.dtype, + ) + + def observation(self, obs: np.ndarray) -> np.ndarray: + full_shape = obs.shape + obs_shape = self.observation_space.shape + addl_dims = len(full_shape) - len(obs_shape) + if addl_dims > 0: + transpose_axes = list(range(addl_dims)) + transpose_axes.extend(a + addl_dims for a in self._transpose_axes) + else: + transpose_axes = self._transpose_axes + return np.transpose(obs, axes=transpose_axes) diff --git a/wrappers/vec_episode_recorder.py b/wrappers/vec_episode_recorder.py new file mode 100644 index 0000000000000000000000000000000000000000..c9e92c1d02f8b9e74d6fa561c77f0374c34b48a1 --- /dev/null +++ b/wrappers/vec_episode_recorder.py @@ -0,0 +1,80 @@ +import numpy as np + +from gym.vector.sync_vector_env import SyncVectorEnv +from gym.wrappers.monitoring.video_recorder import VideoRecorder +from stable_baselines3.common.vec_env.base_vec_env import tile_images +from typing import Optional + +from wrappers.vectorable_wrapper import ( + VecotarableWrapper, + VecEnvObs, + VecEnvStepReturn, +) + + +class VecEpisodeRecorder(VecotarableWrapper): + def __init__(self, env, base_path: str, max_video_length: int = 3600): + super().__init__(env) + self.base_path = base_path + self.max_video_length = max_video_length + self.video_recorder = None + self.recorded_frames = 0 + + def step(self, actions: np.ndarray) -> VecEnvStepReturn: + obs, rew, dones, infos = self.env.step(actions) + # Using first env to record episodes + if self.video_recorder: + self.video_recorder.capture_frame() + self.recorded_frames += 1 + if dones[0] and infos[0].get("episode"): + episode_info = { + k: v.item() if hasattr(v, "item") else v + for k, v in infos[0]["episode"].items() + } + self.video_recorder.metadata["episode"] = episode_info + if dones[0] or self.recorded_frames > self.max_video_length: + self._close_video_recorder() + return obs, rew, dones, infos + + def reset(self) -> VecEnvObs: + obs = self.env.reset() + self._start_video_recorder() + return obs + + def _start_video_recorder(self) -> None: + self._close_video_recorder() + + self.video_recorder = VideoRecorder( + SyncVectorEnvRenderCompat(self.env), + base_path=self.base_path, + ) + + self.video_recorder.capture_frame() + self.recorded_frames = 1 + + def _close_video_recorder(self) -> None: + if self.video_recorder: + self.video_recorder.close() + self.video_recorder = None + + +class SyncVectorEnvRenderCompat(VecotarableWrapper): + def __init__(self, env) -> None: + super().__init__(env) + + def render(self, mode: str = "human") -> Optional[np.ndarray]: + base_env = self.env.unwrapped + if isinstance(base_env, SyncVectorEnv): + imgs = [env.render(mode="rgb_array") for env in base_env.envs] + bigimg = tile_images(imgs) + if mode == "humnan": + import cv2 + + cv2.imshow("vecenv", bigimg[:, :, ::-1]) + cv2.waitKey(1) + elif mode == "rgb_array": + return bigimg + else: + raise NotImplemented(f"Render mode {mode} is not supported") + else: + return self.env.render(mode=mode) diff --git a/wrappers/vectorable_wrapper.py b/wrappers/vectorable_wrapper.py new file mode 100644 index 0000000000000000000000000000000000000000..03df8d1400ab84242353f5dc1288a4394158b941 --- /dev/null +++ b/wrappers/vectorable_wrapper.py @@ -0,0 +1,46 @@ +import numpy as np +from gym import Env, Space, Wrapper + +from stable_baselines3.common.vec_env import VecEnv as SB3VecEnv +from typing import Dict, List, Optional, Type, TypeVar, Tuple, Union + +VecEnvObs = Union[np.ndarray, Dict[str, np.ndarray], Tuple[np.ndarray, ...]] +VecEnvStepReturn = Tuple[VecEnvObs, np.ndarray, np.ndarray, List[Dict]] + + +class VecotarableWrapper(Wrapper): + def __init__(self, env: Env) -> None: + super().__init__(env) + self.num_envs = getattr(env, "num_envs", 1) + self.is_vector_env = getattr(env, "is_vector_env", False) + self.single_observation_space = single_observation_space(env) + self.single_action_space = single_action_space(env) + + def step(self, action) -> VecEnvStepReturn: + return self.env.step(action) + + def reset(self) -> VecEnvObs: + return self.env.reset() + + +VecEnv = Union[VecotarableWrapper, SB3VecEnv] + + +def single_observation_space(env: Union[VecEnv, Env]) -> Space: + return getattr(env, "single_observation_space", env.observation_space) + + +def single_action_space(env: Union[VecEnv, Env]) -> Space: + return getattr(env, "single_action_space", env.action_space) + + +W = TypeVar("W", bound=Wrapper) + + +def find_wrapper(env: VecEnv, wrapper_class: Type[W]) -> Optional[W]: + current = env + while current and current != current.unwrapped: + if isinstance(current, wrapper_class): + return current + current = getattr(current, "env") + return None diff --git a/wrappers/video_compat_wrapper.py b/wrappers/video_compat_wrapper.py new file mode 100644 index 0000000000000000000000000000000000000000..d2e91c7beeee8f5e04bf1c125214aee1e495dcb5 --- /dev/null +++ b/wrappers/video_compat_wrapper.py @@ -0,0 +1,15 @@ +import gym +import numpy as np + +from wrappers.vectorable_wrapper import VecotarableWrapper + + +class VideoCompatWrapper(VecotarableWrapper): + def __init__(self, env: gym.Env) -> None: + super().__init__(env) + + def render(self, mode="human", **kwargs): + r = super().render(mode=mode, **kwargs) + if mode == "rgb_array" and isinstance(r, np.ndarray) and r.dtype != np.uint8: + r = r.astype(np.uint8) + return r