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 index 5ee7616c2cf2173f97daf6cdac9bdf294bee97e8..84f6758bd8f297c105638290e1e93e6eac35bdc9 100644 --- a/README.md +++ b/README.md @@ -1,37 +1,135 @@ --- -library_name: stable-baselines3 +library_name: rl-algo-impls tags: - AntBulletEnv-v0 +- a2c - deep-reinforcement-learning - reinforcement-learning -- stable-baselines3 model-index: -- name: A2C +- name: a2c results: - - task: + - metrics: + - type: mean_reward + value: 1923.79 +/- 147.17 + name: mean_reward + task: type: reinforcement-learning name: reinforcement-learning dataset: name: AntBulletEnv-v0 type: AntBulletEnv-v0 - metrics: - - type: mean_reward - value: 2992.13 +/- 55.54 - name: mean_reward - verified: false --- - # **A2C** Agent playing **AntBulletEnv-v0** -This is a trained model of a **A2C** agent playing **AntBulletEnv-v0** -using the [stable-baselines3 library](https://github.com/DLR-RM/stable-baselines3). -## Usage (with Stable-baselines3) -TODO: Add your code +This is a trained model of a **A2C** agent playing **AntBulletEnv-v0** 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 | AntBulletEnv-v0 | 1 | 1595.64 | 44.4434 | 12 | | [wandb](https://wandb.ai/sgoodfriend/rl-algo-impls-benchmarks/runs/iqaxa6ry) | +| a2c | AntBulletEnv-v0 | 2 | 1923.79 | 147.173 | 12 | * | [wandb](https://wandb.ai/sgoodfriend/rl-algo-impls-benchmarks/runs/dbx3s2ct) | +| a2c | AntBulletEnv-v0 | 3 | 1698.06 | 134.566 | 12 | | [wandb](https://wandb.ai/sgoodfriend/rl-algo-impls-benchmarks/runs/td2ulhkm) | + + +### 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/dbx3s2ct +``` + +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 AntBulletEnv-v0 --seed 2 +``` + +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. -```python -from stable_baselines3 import ... -from huggingface_sb3 import load_from_hub -... +## 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_hyperaparms: + ent_coef: 0 + gae_lambda: 0.9 + gamma: 0.99 + learning_rate: 0.00096 + learning_rate_decay: linear + max_grad_norm: 0.5 + n_steps: 8 + vf_coef: 0.4 +env: AntBulletEnv-v0 +env_hyperparams: + n_envs: 4 + normalize: true +n_timesteps: 2000000 +policy_hyperparams: + init_layers_orthogonal: false + log_std_init: -2 + use_sde: true +seed: 2 +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..02117164b4048badfe959bcb013a87959ca1da69 --- /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", "320x240", "-pix_fmt", "rgb24", "-framerate", "60", "-i", "-", "-vf", "scale=trunc(iw/2)*2:trunc(ih/2)*2", "-vcodec", "libx264", "-pix_fmt", "yuv420p", "-r", "60", "/tmp/tmpfnzc1crw/a2c-AntBulletEnv-v0/replay.mp4"]}, "episode": {"r": 489.45892333984375, "l": 1000, "t": 30.811251}} \ No newline at end of file diff --git a/replay.mp4 b/replay.mp4 index 6dde32d46d65749d5e32d27f85ac25d9d9e3f33c..a0d860c1bfc394b4670a57c48e2f8418426138f4 100644 --- a/replay.mp4 +++ b/replay.mp4 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0de71db0bcf911265d18a06e59b957a8accaaab946bd53ea189534b13147d53b -size 1229831 +oid sha256:4ca84589ecc2ea4c66a8191b001f22c11d1990e4b27f64819a6b15813f1c0279 +size 306959 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-AntBulletEnv-v0-S2-best/model.pth b/saved_models/a2c-AntBulletEnv-v0-S2-best/model.pth new file mode 100644 index 0000000000000000000000000000000000000000..9ef60604b0d73fedcb6049fdde1f2a22a35c2226 --- /dev/null +++ b/saved_models/a2c-AntBulletEnv-v0-S2-best/model.pth @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3dd10987c0fb323975deaa6778598319eea7e928402ac21d5bb7350a1c9f3413 +size 56352 diff --git a/saved_models/a2c-AntBulletEnv-v0-S2-best/norm_obs.npz b/saved_models/a2c-AntBulletEnv-v0-S2-best/norm_obs.npz new file mode 100644 index 0000000000000000000000000000000000000000..de90565274a2167bc9853bf9c238f4af9d5bf7b7 --- /dev/null +++ b/saved_models/a2c-AntBulletEnv-v0-S2-best/norm_obs.npz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f2b4821c7731342206dc0b092be89a0505a1f4e11153f7f109a685c0589557d2 +size 1045 diff --git a/saved_models/a2c-AntBulletEnv-v0-S2-best/norm_reward.npz b/saved_models/a2c-AntBulletEnv-v0-S2-best/norm_reward.npz new file mode 100644 index 0000000000000000000000000000000000000000..3caa96f4e5f4364632abde76c58b73c2237a5f48 --- /dev/null +++ b/saved_models/a2c-AntBulletEnv-v0-S2-best/norm_reward.npz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cafc015154caad7c9e9cdb2ed856be77da5bcc423b756b102c6cb0107d210984 +size 583 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