diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..1bc8b15 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,26 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +### Describe the bug +A clear and concise description of what the bug is. + +### To Reproduce +Code snippet or clear steps to reproduce the behaviour. + +### Expected behavior +A clear and concise description of what you expected to happen. + +### Screenshots +If applicable, add screenshots to help explain your problem. + +### Version + - Version info such as v0.1.0 + +### Additional context +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..3ba13e0 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1 @@ +blank_issues_enabled: false diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..b2de4ff --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +### Is your feature request related to a problem? Please describe. +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +### Describe the solution you'd like +A clear and concise description of what you want to happen. + +### Describe alternatives you've considered +A clear and concise description of any alternative solutions or features you've considered. + +### Additional context +Add any other context or screenshots about the feature request here. diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..d364695 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,8 @@ +# PR Type +[Feature | Fix | Documentation | Refactor | Other] + +# Short Description +Add a short description of what is in this PR. + +# Tests Added +Describe the tests that have been added to ensure the codes correctness, if applicable. diff --git a/.github/workflows/code_checks.yml b/.github/workflows/code_checks.yml new file mode 100644 index 0000000..5f140ae --- /dev/null +++ b/.github/workflows/code_checks.yml @@ -0,0 +1,49 @@ +name: code checks + +on: + push: + branches: + - main + paths: + - .pre-commit-config.yaml + - .github/workflows/code_checks.yml + - '**.py' + - poetry.lock + - pyproject.toml + - '**.ipynb' + pull_request: + branches: + - main + paths: + - .pre-commit-config.yaml + - .github/workflows/code_checks.yml + - '**.py' + - poetry.lock + - pyproject.toml + - '**.ipynb' + +jobs: + run-code-check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4.1.7 + - name: Install and configure Poetry + uses: snok/install-poetry@v1 + with: + virtualenvs-create: true + virtualenvs-in-project: true + - uses: actions/setup-python@v5.1.1 + with: + python-version: '3.9' + cache: 'poetry' + - name: Install dependencies and check code + run: | + poetry env use '3.9' + source .venv/bin/activate + poetry install --with test --all-extras + pre-commit run --all-files + - name: pip-audit (gh-action-pip-audit) + uses: pypa/gh-action-pip-audit@v1.1.0 + with: + virtual-environment: .venv/ + ignore-vulns: GHSA-cqh9-jfqr-h9jj diff --git a/.github/workflows/integration_tests.yml b/.github/workflows/integration_tests.yml new file mode 100644 index 0000000..aff4c30 --- /dev/null +++ b/.github/workflows/integration_tests.yml @@ -0,0 +1,46 @@ +name: integration tests + +on: + push: + branches: + - main + paths: + - .pre-commit-config.yaml + - .github/workflows/code_checks.yml + - .github/workflows/integration_tests.yml + - '**.py' + - '**.ipynb' + - poetry.lock + - pyproject.toml + - '**.rst' + - '**.md' + pull_request: + branches: + - main + paths: + - .pre-commit-config.yaml + - .github/workflows/code_checks.yml + - .github/workflows/integration_tests.yml + - '**.py' + - '**.ipynb' + - poetry.lock + - pyproject.toml + - '**.rst' + - '**.md' + +jobs: + integration-tests: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4.1.1 + - name: Install poetry + run: python3 -m pip install --upgrade pip && python3 -m pip install poetry + - uses: actions/setup-python@v5.0.0 + with: + python-version: '3.9' + - name: Install dependencies and check code + run: | + poetry env use '3.9' + source $(poetry env info --path)/bin/activate + poetry install --with test + pytest -m integration_test diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..4acd636 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,30 @@ +name: publish package + +on: + release: + types: [published] + +jobs: + deploy: + name: Upload release to PyPI + runs-on: ubuntu-latest + environment: + name: pypi + url: https://pypi.org/p/mmlearn + permissions: + id-token: write # IMPORTANT: this permission is mandatory for trusted publishing + steps: + - name: Install apt dependencies + run: | + sudo apt-get update + sudo apt-get install libcurl4-openssl-dev libssl-dev + - uses: actions/checkout@v4.1.1 + - name: Install poetry + run: python3 -m pip install --upgrade pip && python3 -m pip install poetry + - uses: actions/setup-python@v5.1.1 + with: + python-version: '3.9' + - name: Build package + run: poetry build + - name: Publish package + uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c6ec5b9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,131 @@ +# 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 + +# 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/ + +# pycharm +.idea/ + +# vscode +.vscode/ + +# wandb +wandb/ + +# hydra +outputs/ + +# slurm +*slurm-* diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..dc99652 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,60 @@ +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.6.0 # Use the ref you want to point at + hooks: + - id: trailing-whitespace + - id: check-ast + - id: check-builtin-literals + - id: check-docstring-first + - id: check-executables-have-shebangs + - id: debug-statements + - id: end-of-file-fixer + - id: mixed-line-ending + args: [--fix=lf] + - id: detect-private-key + - id: check-yaml + - id: check-toml + + - repo: https://github.com/python-poetry/poetry + rev: 1.8.3 + hooks: + - id: poetry-check + args: [--lock] + + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.5.7 + hooks: + - id: ruff + args: [--fix, --exit-non-zero-on-fix] + types_or: [ python, pyi, jupyter ] + - id: ruff-format + types_or: [ python, pyi, jupyter ] + + - repo: https://github.com/crate-ci/typos + rev: v1.23.6 + hooks: + - id: typos + args: [] + + - repo: https://github.com/pre-commit/mirrors-mypy + rev: v1.11.1 + hooks: + - id: mypy + entry: python3 -m mypy --show-error-codes --pretty --config-file pyproject.toml + types: [python] + exclude: "tests" + + - repo: https://github.com/nbQA-dev/nbQA + rev: 1.8.7 + hooks: + - id: nbqa-ruff + args: [--fix, --exit-non-zero-on-fix] + + - repo: local + hooks: + - id: pytest + name: pytest + language: system + entry: python3 -m pytest -m "not integration_test" + pass_filenames: false + always_run: true diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..4f79bf3 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,128 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +cyclops@vectorinstitute.ai. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct +enforcement ladder](https://github.com/mozilla/diversity). + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at +https://www.contributor-covenant.org/translations. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..23c5d37 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,120 @@ +# Contributing to mmlearn + +Thanks for your interest in contributing to mmlearn! + +To submit PRs, please fill out the PR template along with the PR. If the PR fixes an issue, please include a link to the +PR to the issue. Below are some details around important things to consider before contributing to the library. A table +of contents also appears below for navigation. + +- [Development Practices](#development-practices) +- [Development Requirements](#development-requirements) +- [Coding Guidelines, Formatters, and Checks](#coding-guidelines-formatters-and-checks) +- [Code Documentation](#code-documentation) +- [Tests](#tests) + +## Development Practices + +We use the standard git development flow of branch and merge to main with PRs on GitHub. At least one member of the core +team needs to approve a PR before it can be merged into main. As mentioned above, tests are run automatically on PRs with +a merge target of main. Furthermore, a suite of static code checkers and formatters are also run on said PRs. These also +need to pass for a PR to be eligible for merging into the main branch of the library. Currently, such checks run on python3.9. + +## Development Requirements + +For development and testing, we use [Poetry](https://python-poetry.org/) for dependency management. The library dependencies +and those for development and testing are listed in the `pyproject.toml` file. You may use whatever virtual environment +management tool that you would like. These include conda, poetry itself, and virtualenv. + +The easiest way to create and activate a virtual environment is by using the [virtualenv](https://pypi.org/project/virtualenv/) +package: +```bash +virtualenv "ENV_PATH" +source "ENV_PATH/bin/activate" +pip install --upgrade pip poetry +poetry install --with "dev, test" --all-extras +``` + +Note that the with command is installing all libraries required for the full development workflow. See the `pyproject.toml` +file for additional details as to what is installed with each of these options. + +If you need to update the environment libraries, you should change the requirements in the `pyproject.toml` and then update +the `poetry.lock` using the command `poetry update`. + +## Coding Guidelines, Formatters, and Checks + +For code style, we recommend the [PEP 8 style guide](https://peps.python.org/pep-0008/). + +We use [ruff](https://docs.astral.sh/ruff/) for code formatting and static code analysis. Ruff checks various rules including +[flake8](https://docs.astral.sh/ruff/faq/#how-does-ruff-compare-to-flake8). The pre-commit hooks show errors which you need +to fix before submitting a PR. + +Last but not least, we use type hints in our code which are checked using [mypy](https://mypy.readthedocs.io/en/stable/). +The mypy checks are strictly enforced. That is, all mypy checks must pass or the associated PR will not be merge-able. + +The settings for `mypy` and `ruff` can be found the `pyproject.toml` files and some standard checks are defined directly +in the `.pre-commit-config.yaml` settings. + +All of these checks and formatters are invoked by pre-commit hooks. These hooks are run remotely on GitHub. In order to +ensure that your code conforms to these standards, and, therefore, passes the remote checks, you can install the pre-commit +hooks to be run locally. This is done by running (with your environment active) + +```bash +pre-commit install +``` + +To run the checks, some of which will automatically re-format your code to fit the standards, you can run +```bash +pre-commit run --all-files +``` +It can also be run on a subset of files by omitting the `--all-files` option and pointing to specific files or folders. + +If you're using VS Code for development, pre-commit should setup git hooks that execute the pre-commit checks each time +you check code into your branch through the integrated source-control as well. This will ensure that each of your commits +conform to the desired format before they are run remotely and without needing to remember to run the checks before pushing +to a remote. If this isn't done automatically, you can find instructions for setting up these hooks manually online. + +## Code Documentation + +For code documentation, we try to adhere to the [numpy format](https://numpydoc.readthedocs.io/en/latest/format.html). +For development, __any non-trivial or non-obvious methods added to the library should have a doc string__. For our library +this applies only to code added to the main library in `mmlearn`. Code outside the core library folder, such as tests, +need not incorporate the strict rules of documentation, though clarifying and helpful comments in that code is also +__strongly encouraged__. + +__NOTE__: As a matter of convention choice, classes are documented at the "class" level rather than through their `__init__` +functions. + +If you are using VS Code a very helpful integration is available to facilitate the creation of properly formatted doc-strings +called autoDocstring [VS Code Page](https://marketplace.visualstudio.com/items?itemName=njpwerner.autodocstring) and +[Documentation](https://github.com/NilsJPWerner/autoDocstring). This tool will automatically generate a docstring template +when starting a docstring with triple quotation marks (`"""`). To get the correct format, the following settings should +be prescribed in your VS Code settings JSON: + +```json +{ + "autoDocstring.customTemplatePath": "", + "autoDocstring.docstringFormat": "numpy", + "autoDocstring.generateDocstringOnEnter": true, + "autoDocstring.guessTypes": true, + "autoDocstring.includeExtendedSummary": false, + "autoDocstring.includeName": false, + "autoDocstring.logLevel": "Info", + "autoDocstring.quoteStyle": "\"\"\"", + "autoDocstring.startOnNewLine": true +} +``` + +## Tests + +All tests for the library are housed in the `tests` folder. The unit and integration tests are run using `pytest`. These +tests are automatically run through GitHub integrations on PRs to the main branch of this repository. PRs that fail any +of the tests will not be eligible to be merged until they are fixed. + +To run all tests in the tests folder one only needs to run (with the venv active) +```bash +pytest . +``` +To run a specific test with pytest, one runs +```bash +pytest tests/datasets/test_combined_dataset.py +``` diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md new file mode 100644 index 0000000..4822062 --- /dev/null +++ b/README.md @@ -0,0 +1,126 @@ +# mmlearn +[![code checks](https://github.com/VectorInstitute/mmlearn/actions/workflows/code_checks.yml/badge.svg)](https://github.com/VectorInstitute/mmlearn/actions/workflows/code_checks.yml) +[![integration tests](https://github.com/VectorInstitute/mmlearn/actions/workflows/integration_tests.yml/badge.svg)](https://github.com/VectorInstitute/mmlearn/actions/workflows/integration_tests.yml) +[![license](https://img.shields.io/github/license/VectorInstitute/mmlearn.svg)](https://github.com/VectorInstitute/mmlearn/blob/main/LICENSE) + +This project aims at enabling the evaluation of existing multimodal representation learning methods, as well as facilitating +experimentation and research for new techniques. + +## Quick Start +### Installation +#### Prerequisites +The library requires Python 3.9 or later. We recommend using a virtual environment to manage dependencies. You can create +a virtual environment using the following command: +```bash +python3 -m venv /path/to/new/virtual/environment +source /path/to/new/virtual/environment/bin/activate +``` + +#### Installing binaries +To install the pre-built binaries, run: +```bash +python3 -m pip install mmlearn +``` + +#### Building from source +To install the library from source, run: + +```bash +git clone https://github.com/VectorInstitute/mmlearn.git +cd mmlearn +python3 -m pip install -e . +``` + +### Running Experiments +To run an experiment, create a folder with a similar structure as the [`configs`](configs/) folder. +Then, use the `mmlearn_run` command to run the experiment as defined in a `.yaml` file under the `experiment` folder, like so: +```bash +mmlearn_run --config-dir /path/to/config/dir +experiment= experiment=your_experiment_name +``` +Notice that the config directory refers to the top-level directory containing the `experiment` folder. The experiment +name is the name of the `.yaml` file under the `experiment` folder, without the extension. + +We use [Hydra](https://hydra.cc/docs/intro/) to manage configurations, so you can override any configuration parameter +from the command line. To see the available options and other information, run: +```bash +mmlearn_run --config-dir /path/to/config/dir +experiment= --help +``` + +By default, the `mmlearn_run` command will run the experiment locally. To run the experiment on a SLURM cluster, we use +the [submitit launcher](https://hydra.cc/docs/plugins/submitit_launcher/) plugin built into Hydra. The following is an example +of how to run an experiment on a SLURM cluster: +```bash +mmlearn_run --multirun hydra.launcher.mem_gb=32 hydra.launcher.qos=your_qos hydra.launcher.partition=your_partition hydra.launcher.gres=gpu:4 hydra.launcher.cpus_per_task=8 hydra.launcher.tasks_per_node=4 hydra.launcher.nodes=1 hydra.launcher.stderr_to_stdout=true hydra.launcher.timeout_min=60 '+hydra.launcher.additional_parameters={export: ALL}' --config-dir /path/to/config/dir +experiment= experiment=your_experiment_name +``` +This will submit a job to the SLURM cluster with the specified resources. + +**Note**: After the job is submitted, it is okay to cancel the program with `Ctrl+C`. The job will continue running on +the cluster. You can also add `&` at the end of the command to run it in the background. + + +## Summary of Implemented Methods + + + + + + + + + + + + + + + + + +
Pretraining Methods Notes
+ +Contrastive Pretraining + +Uses the contrastive loss to align the representations from N modalities. Supports sharing of encoders, projection heads +or postprocessing modules (e.g. logit/temperature scaling) across modalities. Also supports multi-task learning with auxiliary +unimodal tasks applied to specific modalities. +
Evaluation Methods Notes
+ +Zero-shot Cross-modal Retrieval + +Evaluates the quality of the learned representations in retrieving the k most similar examples from a different modality, +using recall@k metric. This is applicable to any number of pairs of modalities at once, depending on memory constraints. +
+ +## Components +### Datasets +Every dataset object must return an instance of [`Example`](mmlearn/datasets/core/example.py) with one or more keys/attributes +corresponding to a modality name as specified in the [`Modalities registry`](mmlearn/datasets/core/modalities.py). +The `Example` object must also include an `example_index` attribute/key, which is used, in addition to the dataset index, +to uniquely identify the example. + +
+CombinedDataset + +The [`CombinedDataset`](mmlearn/datasets/core/combined_dataset.py) object is used to combine multiple datasets into one. It +accepts an iterable of `torch.utils.data.Dataset` and/or `torch.utils.data.IterableDataset` objects and returns an `Example` +object from one of the datasets, given an index. Conceptually, the `CombinedDataset` object is a concatenation of the +datasets in the input iterable, so the given index can be mapped to a specific dataset based on the size of the datasets. +As iterable-style datasets do not support random access, the examples from these datasets are returned in order as they +are iterated over. + +The `CombinedDataset` object also adds a `dataset_index` attribute to the `Example` object, corresponding to the index of +the dataset in the input iterable. Every example returned by the `CombinedDataset` will have an `example_ids` attribute, +which is instance of `Example` containing the same keys/attributes as the original example, with the exception of the +`example_index` and `dataset_index` attributes, with values being a tensor of the `dataset_index` and `example_index`. +
+ +### Dataloading +When dealing with multiple datasets with different modalities, the default `collate_fn` of `torch.utils.data.DataLoader` +may not work, as it assumes that all examples have the same keys/attributes. In that case, the [`collate_example_list`](mmlearn/datasets/core/example.py) +function can be used as the `collate_fn` argument of `torch.utils.data.DataLoader`. This function takes a list of `Example` +objects and returns a dictionary of tensors, with all the keys/attributes of the `Example` objects. + +## Contributing + +If you are interested in contributing to the library, please see [CONTRIBUTING.MD](CONTRIBUTING.MD). This file contains +many details around contributing to the code base, including are development practices, code checks, tests, and more. diff --git a/configs/experiment/med_contrastive_pretraining.yaml b/configs/experiment/med_contrastive_pretraining.yaml new file mode 100644 index 0000000..2457d89 --- /dev/null +++ b/configs/experiment/med_contrastive_pretraining.yaml @@ -0,0 +1,120 @@ +# @package _global_ + +defaults: + - /datasets@datasets.train.roco: ROCO + - /datasets/tokenizers@datasets.train.roco.tokenizer: HFCLIPTokenizer + - /datasets/transforms@datasets.train.roco.transform: med_clip_vision_transform + - /datasets@datasets.train.quilt: Quilt + - /datasets/tokenizers@datasets.train.quilt.tokenizer: HFCLIPTokenizer + - /datasets/transforms@datasets.train.quilt.transform: med_clip_vision_transform + - /datasets@datasets.train.mimiciv: MIMICIVCXR + - /datasets/tokenizers@datasets.train.mimiciv.tokenizer: HFCLIPTokenizer + - /datasets/transforms@datasets.train.mimiciv.transform: med_clip_vision_transform + - /datasets@datasets.train.pmcoa: PMCOA + - /datasets/tokenizers@datasets.train.pmcoa.tokenizer: HFCLIPTokenizer + - /datasets/transforms@datasets.train.pmcoa.transform: med_clip_vision_transform + - /datasets@datasets.val.pmcoa: PMCOA + - /datasets/tokenizers@datasets.val.pmcoa.tokenizer: HFCLIPTokenizer + - /datasets/transforms@datasets.val.pmcoa.transform: med_clip_vision_transform + - /modules/encoders@task.encoders.text: HFCLIPTextEncoderWithProjection + - /modules/encoders@task.encoders.rgb: HFCLIPVisionEncoderWithProjection + - /modules/layers@task.postprocessors.norm_and_logit_scale.norm: L2Norm + - /modules/layers@task.postprocessors.norm_and_logit_scale.logit_scale: LearnableLogitScaling + - /modules/losses@task.loss: CLIPLoss + - /modules/optimizers@task.optimizer: AdamW + - /modules/lr_schedulers@task.lr_scheduler.scheduler: CosineAnnealingLR + - /eval_task@task.evaluation_tasks.retrieval.task: ZeroShotCrossModalRetrieval + - /trainer/callbacks@trainer.callbacks.lr_monitor: LearningRateMonitor + - /trainer/callbacks@trainer.callbacks.model_checkpoint: ModelCheckpoint + - /trainer/callbacks@trainer.callbacks.early_stopping: EarlyStopping + - /trainer/logger@trainer.logger.wandb: WandbLogger + - override /task: ContrastivePretraining + - _self_ + +seed: 0 + +datasets: + val: + pmcoa: + split: valid + transform: + job_type: eval + +dataloader: + train: + batch_size: 32 + num_workers: 4 + val: + batch_size: 32 + num_workers: 4 + +task: + postprocessors: + norm_and_logit_scale: + norm: + dim: -1 + logit_scale: + learnable: True + modality_module_mapping: + text: + postprocessor_key: norm_and_logit_scale + rgb: + postprocessor_key: norm_and_logit_scale + optimizer: + betas: + - 0.9 + - 0.98 + lr: 5.0e-5 + weight_decay: 0.1 + eps: 1.0e-6 + lr_scheduler: + scheduler: + T_max: 537_775 + extras: + interval: step + loss: + gather_with_grad: True + evaluation_tasks: + retrieval: + task: + task_specs: + - query_modality: text + target_modality: rgb + top_k: [200] + - query_modality: rgb + target_modality: text + top_k: [200] + run_on_validation: false + run_on_test: true + +trainer: + max_epochs: 100 + precision: 16-mixed + deterministic: False + benchmark: True + sync_batchnorm: False # set to True if using DDP with batchnorm + log_every_n_steps: 100 + accumulate_grad_batches: 4 + check_val_every_n_epoch: 1 + callbacks: + model_checkpoint: + monitor: val/loss + save_top_k: 1 + save_last: True + every_n_epochs: 1 + early_stopping: + monitor: val/loss + patience: 5 + mode: min + +tags: + - ${experiment_name} + - contrastive pretraining + - rgb + - text + - clip + - roco + - quilt + - mimiciv + - pmc_oa + - medmultimodal diff --git a/mmlearn/__init__.py b/mmlearn/__init__.py new file mode 100644 index 0000000..95d8ba4 --- /dev/null +++ b/mmlearn/__init__.py @@ -0,0 +1 @@ +"""Multimodal learning library.""" diff --git a/mmlearn/cli/__init__.py b/mmlearn/cli/__init__.py new file mode 100644 index 0000000..76f44d6 --- /dev/null +++ b/mmlearn/cli/__init__.py @@ -0,0 +1 @@ +"""Command Line Interface for mmlearn.""" diff --git a/mmlearn/cli/_instantiators.py b/mmlearn/cli/_instantiators.py new file mode 100644 index 0000000..925d17b --- /dev/null +++ b/mmlearn/cli/_instantiators.py @@ -0,0 +1,175 @@ +"""Methods for instantiating objects from config. + +This module is for objects that require more complex instantiation logic than +what is provided by Hydra's `instantiate` method. +""" + +import logging +from typing import Any, Dict, List, Optional, Union + +import hydra +from lightning.pytorch.callbacks import Callback +from lightning.pytorch.loggers import Logger +from omegaconf import DictConfig +from torch.utils.data import ( + Dataset, + DistributedSampler, + IterableDataset, +) +from torch.utils.data.sampler import Sampler + +from mmlearn.datasets.core.combined_dataset import CombinedDataset + + +logger = logging.getLogger(__package__) + + +def instantiate_datasets( + cfg: Optional[DictConfig], +) -> Optional[CombinedDataset]: + """Instantiate datasets from config. + + Parameters + ---------- + cfg : DictConfig, optional + A DictConfig object containing dataset configurations. + + Returns + ------- + CombinedDataset or None + The instantiated dataset(s), wrapped in a `CombinedDataset` object. If no + dataset configurations are provided, returns `None`. + """ + if cfg is None: + return None + + datasets: List[Union[Dataset, IterableDataset]] = [] + if "_target_" in cfg: # single dataset + logger.info(f"Instantiating dataset: {cfg._target_}") + datasets.append(hydra.utils.instantiate(cfg, _convert_="partial")) + else: # multiple datasets + for _, ds_conf in cfg.items(): + if "_target_" in ds_conf: + logger.info(f"Instantiating dataset: {ds_conf._target_}") + datasets.append(hydra.utils.instantiate(ds_conf, _convert_="partial")) + else: + logger.warn( + f"Skipping dataset configuration: {ds_conf}. No `_target_` key found." + ) + + return CombinedDataset(datasets) if datasets else None + + +def instantiate_sampler( + cfg: Optional[DictConfig], + dataset: Optional[Union[CombinedDataset, Dataset, IterableDataset]], + requires_distributed_sampler: bool, + distributed_sampler_kwargs: Optional[Dict[str, Any]], +) -> Optional[Sampler]: + """Instantiate sampler from config. + + Parameters + ---------- + cfg : DictConfig, optional + The configuration for the sampler. + dataset : Union[CombinedDataset, Dataset, IterableDataset], optional + The dataset for which to instantiate the sampler. + requires_distributed_sampler : bool + Whether a distributed sampler is required. This is typically True when + the lightning trainer is using a Parallel strategy and the + `use_distributed_sampler` flag is set to True. + distributed_sampler_kwargs : Dict[str, Any], optional + Additional keyword arguments to pass to the distributed sampler. + + Returns + ------- + Sampler or None + The instantiated sampler or None if no sampler has been instantiated. + """ + sampler: Optional[Sampler] = None + if cfg is not None: + sampler = hydra.utils.instantiate( + cfg, + dataset=dataset, + **distributed_sampler_kwargs + if requires_distributed_sampler + or "CombinedDatasetRatioSampler" in cfg._target_ + else {}, + ) + assert isinstance( + sampler, Sampler + ), f"Expected a `torch.utils.data.Sampler` object but got {type(sampler)}." + + if sampler is None and requires_distributed_sampler: + sampler = DistributedSampler(dataset, **distributed_sampler_kwargs) + + return sampler + + +def instantiate_callbacks(cfg: Optional[DictConfig]) -> Optional[List[Callback]]: + """Instantiate callbacks from config. + + Parameters + ---------- + cfg : DictConfig, optional + A DictConfig object containing callback configurations. + + Returns + ------- + List[Callback] or None + A list of instantiated callbacks or None if no callback configurations + are provided. + """ + if cfg is None: + return None + + if not isinstance(cfg, DictConfig): + raise TypeError( + f"Expected `cfg` to be an instance of `DictConfig` but got {type(cfg)}." + ) + + callbacks: List[Callback] = [] + for _, cb_conf in cfg.items(): + if isinstance(cb_conf, DictConfig) and "_target_" in cb_conf: + obj = hydra.utils.instantiate(cb_conf, _convert_="partial") + if not isinstance(obj, Callback): + raise TypeError( + f"Expected a pytorch lightning `Callback` object but got {type(obj)}." + ) + callbacks.append(obj) + + return callbacks + + +def instantiate_loggers(cfg: Optional[DictConfig]) -> Optional[List[Logger]]: + """Instantiate loggers from config. + + Parameters + ---------- + cfg : DictConfig, optional + A DictConfig object containing logger configurations. + + Returns + ------- + List[Logger] or None + A list of instantiated loggers or None if no configurations are provided. + """ + if cfg is None: + return None + + if not isinstance(cfg, DictConfig): + raise TypeError( + f"Expected `cfg` to be an instance of `DictConfig` but got {type(cfg)}." + ) + + logger: List[Logger] = [] + for _, lg_conf in cfg.items(): + if isinstance(lg_conf, DictConfig) and "_target_" in lg_conf: + obj = hydra.utils.instantiate(lg_conf, _convert_="partial") + if not isinstance(obj, Logger): + raise TypeError( + f"Expected a pytorch lightning `Logger` object but got {type(obj)}." + ) + logger.append(obj) + + return logger diff --git a/mmlearn/cli/run.py b/mmlearn/cli/run.py new file mode 100644 index 0000000..e1501e0 --- /dev/null +++ b/mmlearn/cli/run.py @@ -0,0 +1,145 @@ +"""Main entry point for training and evaluation.""" + +import copy +import logging +from typing import Optional + +import hydra +import lightning as L # noqa: N812 +import torch +from lightning.pytorch.loggers.wandb import WandbLogger +from lightning.pytorch.trainer import Trainer +from omegaconf import OmegaConf +from pytorch_lightning.utilities import rank_zero_only +from torch.utils.data import DataLoader + +from mmlearn.cli._instantiators import ( + instantiate_callbacks, + instantiate_datasets, + instantiate_loggers, + instantiate_sampler, +) +from mmlearn.conf import JobType, MMLearnConf, hydra_main +from mmlearn.datasets.core import * # noqa: F403 +from mmlearn.datasets.processors import * # noqa: F403 +from mmlearn.modules.encoders import * # noqa: F403 +from mmlearn.modules.layers import * # noqa: F403 +from mmlearn.modules.losses import * # noqa: F403 +from mmlearn.modules.lr_schedulers import * # noqa: F403 +from mmlearn.modules.metrics import * # noqa: F403 +from mmlearn.tasks import * # noqa: F403 + + +logger = logging.getLogger(__package__) + + +@hydra_main( + config_path="pkg://mmlearn.conf", config_name="base_config", version_base=None +) +def main(cfg: MMLearnConf) -> None: # noqa: PLR0912 + """Entry point for training or evaluation.""" + cfg_copy = copy.deepcopy(cfg) # copy of the config for logging + + L.seed_everything(cfg.seed, workers=True) + torch.set_float32_matmul_precision("high") + + # setup trainer first so that we can get some variables for distributed training + callbacks = instantiate_callbacks(cfg.trainer.get("callbacks")) + cfg.trainer["callbacks"] = None # will be replaced with the instantiated object + loggers = instantiate_loggers(cfg.trainer.get("logger")) + cfg.trainer["logger"] = None + trainer: Trainer = hydra.utils.instantiate( + cfg.trainer, callbacks=callbacks, logger=loggers, _convert_="all" + ) + assert isinstance( + trainer, Trainer + ), "Trainer must be an instance of `lightning.pytorch.trainer.Trainer`" + + if rank_zero_only.rank == 0 and loggers is not None: # update wandb config + for trainer_logger in loggers: + if isinstance(trainer_logger, WandbLogger): + trainer_logger.experiment.config.update( + OmegaConf.to_container(cfg_copy, resolve=True, enum_to_str=True), + allow_val_change=True, + ) + trainer.print(OmegaConf.to_yaml(cfg_copy, resolve=True)) + + requires_distributed_sampler = ( + trainer.distributed_sampler_kwargs is not None + and trainer._accelerator_connector.use_distributed_sampler + ) + if requires_distributed_sampler: # we handle distributed samplers + trainer._accelerator_connector.use_distributed_sampler = False + + # prepare dataloaders + if cfg.job_type == JobType.train: + train_dataset = instantiate_datasets(cfg.datasets.train) + assert ( + train_dataset is not None + ), "Train dataset (`cfg.datasets.train`) is required for training." + + train_sampler = instantiate_sampler( + cfg.dataloader.train.get("sampler"), + train_dataset, + requires_distributed_sampler=requires_distributed_sampler, + distributed_sampler_kwargs=trainer.distributed_sampler_kwargs, + ) + cfg.dataloader.train["sampler"] = None # replaced with the instantiated object + train_loader: DataLoader = hydra.utils.instantiate( + cfg.dataloader.train, dataset=train_dataset, sampler=train_sampler + ) + + val_loader: Optional[DataLoader] = None + val_dataset = instantiate_datasets(cfg.datasets.val) + if val_dataset is not None: + val_sampler = instantiate_sampler( + cfg.dataloader.val.get("sampler"), + val_dataset, + requires_distributed_sampler=requires_distributed_sampler, + distributed_sampler_kwargs=trainer.distributed_sampler_kwargs, + ) + cfg.dataloader.val["sampler"] = None + val_loader = hydra.utils.instantiate( + cfg.dataloader.val, dataset=val_dataset, sampler=val_sampler + ) + else: + test_dataset = instantiate_datasets(cfg.datasets.test) + assert ( + test_dataset is not None + ), "Test dataset (`cfg.datasets.test`) is required for evaluation." + + test_sampler = instantiate_sampler( + cfg.dataloader.test.get("sampler"), + test_dataset, + requires_distributed_sampler=requires_distributed_sampler, + distributed_sampler_kwargs=trainer.distributed_sampler_kwargs, + ) + cfg.dataloader.test["sampler"] = None + test_loader = hydra.utils.instantiate( + cfg.dataloader.test, dataset=test_dataset, sampler=test_sampler + ) + + # setup task module + if cfg.task is None or "_target_" not in cfg.task: + raise ValueError( + "Expected a non-empty config for `cfg.task` with a `_target_` key. " + f"But got: {cfg.task}" + ) + logger.info(f"Instantiating task module: {cfg.task['_target_']}") + model: L.LightningModule = hydra.utils.instantiate(cfg.task, _convert_="partial") + assert isinstance(model, L.LightningModule), "Task must be a `LightningModule`" + model.strict_loading = cfg.strict_loading + + # compile model + model = torch.compile(model, **OmegaConf.to_object(cfg.torch_compile_kwargs)) + + if cfg.job_type == JobType.train: + trainer.fit( + model, train_loader, val_loader, ckpt_path=cfg.resume_from_checkpoint + ) + elif cfg.job_type == JobType.eval: + trainer.test(model, test_loader, ckpt_path=cfg.resume_from_checkpoint) + + +if __name__ == "__main__": + main() diff --git a/mmlearn/conf/__init__.py b/mmlearn/conf/__init__.py new file mode 100644 index 0000000..63d07a2 --- /dev/null +++ b/mmlearn/conf/__init__.py @@ -0,0 +1,478 @@ +"""Hydra/Hydra-zen-based configurations.""" + +import functools +from dataclasses import dataclass, field +from enum import Enum +from pathlib import Path +from types import ModuleType +from typing import Any, Callable, Dict, List, Optional + +import hydra +import lightning.pytorch.callbacks as lightning_callbacks +import lightning.pytorch.loggers as lightning_loggers +import lightning.pytorch.trainer as lightning_trainer +import torch.nn.modules.loss as torch_losses +import torch.optim as torch_optim +import torch.utils.data +from hydra.conf import HelpConf, HydraConf, JobConf, RunDir, SweepDir +from hydra.core.config_store import ConfigStore +from hydra.main import _UNSPECIFIED_ +from hydra.types import TaskFunction +from hydra_zen import ZenStore, builds, store +from lightning.pytorch.loggers.wandb import _WANDB_AVAILABLE +from omegaconf import II, MISSING, SI, DictConfig + +from mmlearn.datasets.core.example import collate_example_list + + +def _get_default_ckpt_dir() -> Any: + """Get the default checkpoint directory.""" + return SI("/checkpoint/${oc.env:USER}/${oc.env:SLURM_JOB_ID}") + + +_DataLoaderConf = builds( + torch.utils.data.DataLoader, + populate_full_signature=True, + dataset=MISSING, + pin_memory=True, + collate_fn=collate_example_list, + hydra_convert="all", +) + + +class JobType(str, Enum): + """Type of the job.""" + + train = "train" + eval = "eval" + + +@dataclass +class DatasetConf: + """Configuration template for the datasets.""" + + train: Optional[Any] = field( + default=None, + metadata={"help": "Configuration for the training dataset."}, + ) + val: Optional[Any] = field( + default=None, metadata={"help": "Configuration for the validation dataset."} + ) + test: Optional[Any] = field( + default=None, + metadata={"help": "Configuration for the test dataset."}, + ) + + +@dataclass +class DataLoaderConf: + """Configuration for the dataloader.""" + + train: Any = field( + default_factory=_DataLoaderConf, + metadata={"help": "Configuration for the training dataloader."}, + ) + val: Any = field( + default_factory=_DataLoaderConf, + metadata={"help": "Configuration for the validation dataloader."}, + ) + test: Any = field( + default_factory=_DataLoaderConf, + metadata={"help": "Configuration for the test dataloader."}, + ) + + +@dataclass +class MMLearnConf: + """Top-level configuration for mmlearn experiments.""" + + defaults: List[Any] = field( + default_factory=lambda: [ + "_self_", # See https://hydra.cc/docs/1.2/upgrades/1.0_to_1.1/default_composition_order for more information + {"datasets@datasets.train": MISSING if SI("job_type") == "train" else None}, + {"datasets@datasets.val": None}, + {"datasets@datasets.test": MISSING if II("job_type") == "eval" else None}, + {"task": MISSING}, + {"override hydra/launcher": "submitit_slurm"}, + ] + ) + experiment_name: str = field( + default=MISSING, metadata={"help": "Name of the experiment."} + ) + job_type: JobType = field( + default=JobType.train, metadata={"help": "Type of the job."} + ) + seed: Optional[int] = field( + default=None, metadata={"help": "Seed for the random number generators."} + ) + datasets: DatasetConf = field( + default_factory=DatasetConf, + metadata={"help": "Configuration for the datasets."}, + ) + dataloader: DataLoaderConf = field( + default_factory=DataLoaderConf, + metadata={"help": "Configuration for the dataloader."}, + ) + task: Any = field( + default=MISSING, + metadata={"help": "Configuration for the task, typically a LightningModule."}, + ) + trainer: Any = field( + default_factory=builds( + lightning_trainer.Trainer, + populate_full_signature=True, + enable_model_summary=True, + enable_progress_bar=True, + enable_checkpointing=True, + default_root_dir=_get_default_ckpt_dir(), + ), + metadata={"help": "Configuration for the Trainer."}, + ) + tags: Optional[List[str]] = field( + default_factory=lambda: [II("experiment_name")], + metadata={"help": "Tags for the experiment. Useful for wandb logging."}, + ) + resume_from_checkpoint: Optional[Path] = field( + default=None, + metadata={"help": "Path to the checkpoint to resume training from."}, + ) + strict_loading: bool = field( + default=True, + metadata={"help": "Whether to strictly enforce loading of model weights."}, + ) + torch_compile_kwargs: Dict[str, Any] = field( + default_factory=lambda: { + "disable": True, + "fullgraph": False, + "dynamic": None, + "backend": "inductor", + "mode": None, + "options": None, + }, + metadata={"help": "Configuration for torch.jit.compile."}, + ) + hydra: HydraConf = HydraConf( + searchpath=["pkg://mmlearn/conf", "file://./configs"], + run=RunDir( + dir=SI("./outputs/${experiment_name}/${now:%Y-%m-%d}/${now:%H-%M-%S}") + ), + sweep=SweepDir( + dir=SI("./outputs/${experiment_name}/${now:%Y-%m-%d}/${now:%H-%M-%S}"), + subdir=SI("${hydra.job.num}_${hydra.job.id}"), + ), + help=HelpConf( + app_name="mmlearn", + header="mmlearn: A modular framework for research on multimodal representation learning.", + ), + job=JobConf( + name=II("experiment_name"), + env_set={ + "NCCL_IB_DISABLE": "1", # disable InfiniBand (the Vector cluster does not have it) + "TORCH_NCCL_ASYNC_ERROR_HANDLING": "3", + "HYDRA_FULL_ERROR": "1", + }, + ), + ) + + +cs = ConfigStore.instance() + +cs.store( + name="base_config", + node=MMLearnConf, + package="_global_", + provider="mmlearn", +) + + +#################### External Modules #################### +external_store = ZenStore(name="external_store", deferred_hydra_store=False) + + +def register_external_modules( + module: ModuleType, + group: str, + name: Optional[str] = None, + package: Optional[str] = None, + provider: Optional[str] = None, + base_cls: Optional[type] = None, + ignore_cls: Optional[List[type]] = None, + ignore_prefix: Optional[str] = None, + **kwargs_for_builds: Any, +) -> None: + """Add all classes in an external module to a ZenStore. + + Parameters + ---------- + module : ModuleType + The module to add classes from. + group : str + The config group to add the classes to. + name : str, optional, default=None + The name to give to the dynamically-generated configs. If `None`, the + class name is used. + package : str, optional, default=None + The package to add the configs to. + provider : str, optional, default=None + The provider to add the configs to. + base_cls : type, optional, default=None + The base class to filter classes by. The base class is also excluded from + the configs. + ignore_cls : List[type], optional, default=None + List of classes to ignore. + ignore_prefix : str, optional, default=None + Ignore classes whose names start with this prefix. + kwargs_for_builds : Any + Additional keyword arguments to pass to `hydra_zen.builds`. + + """ + for key, cls in module.__dict__.items(): + if ( + isinstance(cls, type) + and (base_cls is None or issubclass(cls, base_cls)) + and cls != base_cls + and (ignore_cls is None or cls not in ignore_cls) + and (ignore_prefix is None or not key.startswith(ignore_prefix)) + ): + external_store( + builds(cls, populate_full_signature=True, **kwargs_for_builds), + name=name or key, + group=group, + package=package, + provider=provider, + ) + + +register_external_modules( + torch_optim, + group="modules/optimizers", + provider="torch", + base_cls=torch_optim.Optimizer, + zen_partial=True, +) + +# NOTE: learning rate schedulers require partial instantiation (for adding the optimizer +# at runtime) and most of them have more than one required argument. When using +# `zen_partial=True`, hydra-zen will remove all arguments that don't have a default +# value. This is why we need to manually specify the required arguments for each +# learning rate scheduler. +external_store( + builds( + torch_optim.lr_scheduler.StepLR, + populate_full_signature=True, + zen_partial=True, + optimizer=MISSING, + step_size=MISSING, + ), + name="StepLR", + group="modules/lr_schedulers", + provider="torch", +) +external_store( + builds( + torch_optim.lr_scheduler.MultiStepLR, + populate_full_signature=True, + zen_partial=True, + optimizer=MISSING, + milestones=MISSING, + ), + name="MultiStepLR", + group="modules/lr_schedulers", + provider="torch", +) +external_store( + builds( + torch_optim.lr_scheduler.ExponentialLR, + populate_full_signature=True, + zen_partial=True, + optimizer=MISSING, + gamma=MISSING, + ), + name="ExponentialLR", + group="modules/lr_schedulers", + provider="torch", +) +external_store( + builds( + torch_optim.lr_scheduler.CosineAnnealingLR, + populate_full_signature=True, + zen_partial=True, + optimizer=MISSING, + T_max=MISSING, + ), + name="CosineAnnealingLR", + group="modules/lr_schedulers", + provider="torch", +) +external_store( + builds( + torch_optim.lr_scheduler.CyclicLR, + populate_full_signature=True, + zen_partial=True, + optimizer=MISSING, + base_lr=MISSING, + max_lr=MISSING, + ), + name="CyclicLR", + group="modules/lr_schedulers", + provider="torch", +) +external_store( + builds( + torch_optim.lr_scheduler.OneCycleLR, + populate_full_signature=True, + zen_partial=True, + optimizer=MISSING, + max_lr=MISSING, + ), + name="OneCycleLR", + group="modules/lr_schedulers", + provider="torch", +) +external_store( + builds( + torch_optim.lr_scheduler.ReduceLROnPlateau, + populate_full_signature=True, + zen_partial=True, + optimizer=MISSING, + ), + name="ReduceLROnPlateau", + group="modules/lr_schedulers", + provider="torch", +) +external_store( + builds( + torch_optim.lr_scheduler.LinearLR, + populate_full_signature=True, + zen_partial=True, + optimizer=MISSING, + ), + name="LinearLR", + group="modules/lr_schedulers", + provider="torch", +) +external_store( + builds( + torch_optim.lr_scheduler.PolynomialLR, + populate_full_signature=True, + zen_partial=True, + optimizer=MISSING, + ), + name="PolynomialLR", + group="modules/lr_schedulers", + provider="torch", +) +external_store( + builds( + torch_optim.lr_scheduler.CosineAnnealingWarmRestarts, + populate_full_signature=True, + zen_partial=True, + optimizer=MISSING, + T_0=MISSING, + ), + name="CosineAnnealingWarmRestarts", + group="modules/lr_schedulers", + provider="torch", +) + +register_external_modules( + torch_losses, + group="modules/losses", + provider="torch", + base_cls=torch_losses._Loss, + ignore_prefix="_", +) + +register_external_modules( + torch.utils.data, + group="dataloader/sampler", + provider="torch", + base_cls=torch.utils.data.Sampler, + hydra_convert="all", +) + +# NOTE: as of v2.3.3, the `device` filled for StochasticWeightAveraging has a default +# value of torch.device("cpu"), which is not a serializable type. +# ModelCheckpoint is configured separately so as to set some reasonable defaults. +register_external_modules( + lightning_callbacks, + group="trainer/callbacks", + provider="lightning", + base_cls=lightning_callbacks.Callback, + ignore_cls=[ + lightning_callbacks.StochasticWeightAveraging, + lightning_callbacks.ModelCheckpoint, + ], +) +external_store( + builds( + lightning_callbacks.ModelCheckpoint, + populate_full_signature=True, + dirpath=_get_default_ckpt_dir(), + ), + name="ModelCheckpoint", + group="trainer/callbacks", + provider="lightning", +) + +if _WANDB_AVAILABLE: + register_external_modules( + lightning_loggers, + group="trainer/logger", + provider="lightning", + base_cls=lightning_loggers.Logger, + ignore_cls=[lightning_loggers.WandbLogger], + ) + external_store( + builds( + lightning_loggers.WandbLogger, + populate_full_signature=True, + name=II("experiment_name"), + save_dir=SI("${hydra:runtime.output_dir}"), + dir=SI("${hydra:runtime.output_dir}"), + project=SI("${oc.env:WANDB_PROJECT}"), + resume="allow", + tags=II("tags"), + job_type=II("job_type"), + ), + name="WandbLogger", + group="trainer/logger", + provider="lightning", + ) + + +#################### Custom Hydra Main Decorator #################### +def hydra_main( + config_path: Optional[str] = _UNSPECIFIED_, + config_name: Optional[str] = None, + version_base: Optional[str] = _UNSPECIFIED_, +) -> Callable[[TaskFunction], Any]: + """Add hydra_zen configs to hydra's global config store. + + Custom hydra main decorator that adds hydra_zen configs to global store. + + Parameters + ---------- + config_path : + The config path, a directory where Hydra will search for config files. + This path is added to Hydra's searchpath. Relative paths are interpreted + relative to the declaring python file. Alternatively, you can use the prefix + `pkg://` to specify a python package to add to the searchpath. + If config_path is None no directory is added to the Config search path. + config_name : + The name of the config (usually the file name without the .yaml extension) + """ + + def main_decorator(task_function: TaskFunction) -> Callable[[], None]: + @functools.wraps(task_function) + def decorated_main(cfg_passthrough: Optional[DictConfig] = None) -> Any: + store.add_to_hydra_store() + return hydra.main( + config_path=config_path, + config_name=config_name, + version_base=version_base, + )(task_function)(cfg_passthrough) + + return decorated_main + + return main_decorator diff --git a/mmlearn/constants.py b/mmlearn/constants.py new file mode 100644 index 0000000..6edc389 --- /dev/null +++ b/mmlearn/constants.py @@ -0,0 +1,3 @@ +"""Constants.""" + +EXAMPLE_INDEX_KEY = "example_index" diff --git a/mmlearn/datasets/__init__.py b/mmlearn/datasets/__init__.py new file mode 100644 index 0000000..e2a5e61 --- /dev/null +++ b/mmlearn/datasets/__init__.py @@ -0,0 +1,32 @@ +"""Datasets.""" + +from mmlearn.datasets.chexpert import CheXpert +from mmlearn.datasets.ego4d import Ego4DDataset +from mmlearn.datasets.imagenet import ImageNet +from mmlearn.datasets.librispeech import LibriSpeech +from mmlearn.datasets.llvip import LLVIPDataset +from mmlearn.datasets.medvqa import MedVQA +from mmlearn.datasets.mimiciv_cxr import MIMICIVCXR +from mmlearn.datasets.nihcxr import NIHCXR +from mmlearn.datasets.nyuv2 import NYUv2Dataset +from mmlearn.datasets.pmcoa import PMCOA +from mmlearn.datasets.quilt import Quilt +from mmlearn.datasets.roco import ROCO +from mmlearn.datasets.sunrgbd import SUNRGBDDataset + + +__all__ = [ + "CheXpert", + "Ego4DDataset", + "ImageNet", + "LibriSpeech", + "LLVIPDataset", + "MedVQA", + "MIMICIVCXR", + "NIHCXR", + "NYUv2Dataset", + "PMCOA", + "Quilt", + "ROCO", + "SUNRGBDDataset", +] diff --git a/mmlearn/datasets/chexpert.py b/mmlearn/datasets/chexpert.py new file mode 100644 index 0000000..6a4cf0d --- /dev/null +++ b/mmlearn/datasets/chexpert.py @@ -0,0 +1,117 @@ +"""CheXpert Dataset.""" + +import json +import os +from typing import Callable, List, Literal, Optional + +import torch +from hydra_zen import MISSING, store +from PIL import Image +from torch.utils.data import Dataset +from torchvision.transforms import CenterCrop, Compose, Resize, ToTensor + +from mmlearn.constants import EXAMPLE_INDEX_KEY +from mmlearn.datasets.core import Modalities +from mmlearn.datasets.core.example import Example + + +_LABELS = [ + "Enlarged Cardiomediastinum", + "Cardiomegaly", + "Lung Opacity", + "Lung Lesion", + "Edema", + "Consolidation", + "Pneumonia", + "Atelectasis", + "Pneumothorax", + "Pleural Effusion", + "Pleural Other", + "Fracture", + "Support Devices", + "No Finding", +] + + +def text_labels() -> List[str]: + """Return a list of the CheXpert dataset's textual labels.""" + return _LABELS + + +@store( + group="datasets", + provider="mmlearn", + root_dir=os.getenv("CHEXPERT_ROOT_DIR", MISSING), + split="train", +) +class CheXpert(Dataset[Example]): + """CheXpert dataset. + + Each datapoint is a pair of (image, target label). + + Parameters + ---------- + data_root : str + Directory which contains json files stating all dataset entries. + split : {"train", "valid"} + Dataset split. + labeler : {"chexpert", "chexbert", "vchexbert"}, optional, default=None + Labeler used to extract labels from the training images. + Valid split has no labeler, labeling for valid split was done by + human radiologists. + transform : callable, optional, default=None + Transform applied to images. + """ + + def __init__( + self, + root_dir: str, + split: Literal["train", "valid"], + labeler: Optional[Literal["chexpert", "chexbert", "vchexbert"]] = None, + transform: Optional[Callable[[Image.Image], torch.Tensor]] = None, + ) -> None: + """Initialize the dataset.""" + assert split in ["train", "valid"], f"split {split} is not available." + assert ( + labeler in ["chexpert", "chexbert", "vchexbert"] or labeler is None + ), f"labeler {labeler} is not available." + assert ( + callable(transform) or transform is None + ), "transform is not callable or None." + + if split == "valid": + data_file = f"{split}_data.json" + elif split == "train": + data_file = f"{labeler}_{split}_data.json" + data_path = os.path.join(root_dir, data_file) + + assert os.path.isfile(data_path), f"entries file does not exist: {data_path}." + + with open(data_path, "rb") as file: + entries = json.load(file) + self.entries = entries + + if transform is not None: + self.transform = transform + else: + self.transform = Compose([Resize(224), CenterCrop(224), ToTensor()]) + + def __getitem__(self, idx: int) -> Example: + """Return the idx'th datapoint.""" + entry = self.entries[idx] + image = Image.open(entry["image_path"]).convert("RGB") + image = self.transform(image) + label = torch.tensor(entry["label"]) + + return Example( + { + Modalities.RGB: image, + Modalities.RGB.target: label, + "qid": entry["qid"], + EXAMPLE_INDEX_KEY: idx, + } + ) + + def __len__(self) -> int: + """Return the length of the dataset.""" + return len(self.entries) diff --git a/mmlearn/datasets/core/__init__.py b/mmlearn/datasets/core/__init__.py new file mode 100644 index 0000000..03fab01 --- /dev/null +++ b/mmlearn/datasets/core/__init__.py @@ -0,0 +1,26 @@ +"""Modules for core dataloading functionality.""" + +from mmlearn.datasets.core.combined_dataset import CombinedDataset +from mmlearn.datasets.core.example import ( + Example, + collate_example_list, + find_matching_indices, +) +from mmlearn.datasets.core.modalities import ModalityRegistry +from mmlearn.datasets.core.samplers import ( + CombinedDatasetRatioSampler, + DistributedEvalSampler, +) + + +Modalities = ModalityRegistry() + +__all__ = [ + "CombinedDataset", + "Example", + "collate_example_list", + "find_matching_indices", + "CombinedDatasetRatioSampler", + "DistributedEvalSampler", + "Modalities", +] diff --git a/mmlearn/datasets/core/combined_dataset.py b/mmlearn/datasets/core/combined_dataset.py new file mode 100644 index 0000000..b5d7f1c --- /dev/null +++ b/mmlearn/datasets/core/combined_dataset.py @@ -0,0 +1,104 @@ +"""Wrapper for combining multiple datasets into one.""" + +import bisect +from collections.abc import Iterator +from typing import Iterable, Union + +import numpy as np +from torch.utils._pytree import tree_flatten +from torch.utils.data import Dataset, IterableDataset + +from mmlearn.datasets.core.example import Example + + +class CombinedDataset(Dataset[Example]): + """Combine multiple datasets into one. + + This class is similar to `torch.utils.data.ConcatDataset` but allows for + combining iterable-style datasets with map-style datasets. The iterable-style + datasets must implement the `__len__` method, which is used to determine the + total length of the combined dataset. When an index is passed to the combined + dataset, the dataset that contains the example at that index is determined and + the example is retrieved from that dataset. Since iterable-style datasets do + not support random access, the examples are retrieved sequentially from the + iterable-style datasets. When the end of an iterable-style dataset is reached, + the iterator is reset and the next example is retrieved from the beginning of + the dataset. + + + Parameters + ---------- + datasets : Iterable[Union[Dataset, IterableDataset]] + Iterable of datasets to combine. + + """ + + def __init__( + self, datasets: Iterable[Union[Dataset[Example], IterableDataset[Example]]] + ) -> None: + """Initialize the combined dataset.""" + self.datasets, _ = tree_flatten(datasets) + if not all( + isinstance(dataset, (Dataset, IterableDataset)) for dataset in self.datasets + ): + raise TypeError( + "Expected argument `datasets` to be an iterable of `Dataset` or " + f"`IterableDataset` instances, but found: {self.datasets}", + ) + if len(self.datasets) == 0: + raise ValueError( + "Expected a non-empty iterable of datasets but found an empty iterable", + ) + + self.cumulative_sizes: list[int] = np.cumsum( + [len(dataset) for dataset in self.datasets] + ).tolist() + self._iterators: list[Iterator[Example]] = [] + self._iter_dataset_mapping: dict[int, int] = {} + + # create iterators for iterable datasets and map dataset index to iterator index + for idx, dataset in enumerate(self.datasets): + if isinstance(dataset, IterableDataset): + self._iterators.append(iter(dataset)) + self._iter_dataset_mapping[idx] = len(self._iterators) - 1 + + def __getitem__(self, idx: int) -> Example: + """Return an example from the combined dataset.""" + if idx < 0: # handle negative indices + if -idx > len(self): + raise IndexError( + f"Index {idx} is out of bounds for the combined dataset with " + f"length {len(self)}", + ) + idx = len(self) + idx + + dataset_idx = bisect.bisect_right(self.cumulative_sizes, idx) + + curr_dataset = self.datasets[dataset_idx] + if isinstance(curr_dataset, IterableDataset): + iter_idx = self._iter_dataset_mapping[dataset_idx] + try: + example = next(self._iterators[iter_idx]) + except StopIteration: + self._iterators[iter_idx] = iter(curr_dataset) + example = next(self._iterators[iter_idx]) + else: + if dataset_idx == 0: + example_idx = idx + else: + example_idx = idx - self.cumulative_sizes[dataset_idx - 1] + example = curr_dataset[example_idx] + + if not isinstance(example, Example): + raise TypeError( + "Expected dataset examples to be instances of `Example` " + f"but found {type(example)}", + ) + example.dataset_index = dataset_idx + example.create_ids() + + return example + + def __len__(self) -> int: + """Return the total number of examples in the combined dataset.""" + return self.cumulative_sizes[-1] diff --git a/mmlearn/datasets/core/example.py b/mmlearn/datasets/core/example.py new file mode 100644 index 0000000..6a31bdf --- /dev/null +++ b/mmlearn/datasets/core/example.py @@ -0,0 +1,230 @@ +"""Module for example-related classes and functions.""" + +from collections import OrderedDict +from collections.abc import MutableMapping +from typing import Any, Hashable, Optional + +import torch +from lightning.fabric.utilities import rank_zero_warn +from torch.utils.data import default_collate + + +class Example(OrderedDict[Any, Any]): + """A representation of a single example from a dataset. + + This class is a subclass of `OrderedDict` and provides attribute-style access. + This means that `example["text"]` and `example.text` are equivalent. + All datasets in this library return examples as `Example` objects. + + + Parameters + ---------- + init_dict : Mapping[str, Any], optional, default=None + Dictionary to init `Example` class with. + + Examples + -------- + >>> example = Example({"text": torch.tensor(2)}) + >>> example.text.zero_() + tensor(0) + >>> example.context = torch.tensor(4) # set custom attributes after initialization + """ + + def __init__( + self, + init_dict: Optional[MutableMapping[Hashable, Any]] = None, + ) -> None: + """Initialize a `Example` object.""" + if init_dict is None: + init_dict = {} + super().__init__(init_dict) + + def create_ids(self) -> None: + """Create a unique id for the example from the dataset and example index. + + The example id is a tensor of shape `(2,)` (or a tuple) that contains the + dataset index and example index. It can be used to (re-)identify pairs of + examples from different modalities. + The example must have the `example_index` (usually set/returned by the dataset) + and `dataset_index` (usually set by the `dataset.combined_dataset.CombinedDataset` + object) attributes set before calling this method. + The matching example ids can be found using the `dataset.example.find_matching_indices` + method. + + Warns + ----- + UserWarning + If the `example_index` and `dataset_index` attributes are not set. + """ # noqa: W505 + if hasattr(self, "example_index") and hasattr(self, "dataset_index"): + self.example_ids = { + key: torch.tensor([self.dataset_index, self.example_index]) + for key in self.keys() + if key not in ("example_ids", "example_index", "dataset_index") + } + else: + rank_zero_warn( + "Cannot create `example_ids` without `example_index` and `dataset_index` " + "attributes. Set these attributes before calling `create_ids`. " + "No `example_ids` was created.", + stacklevel=2, + category=UserWarning, + ) + + def __getattr__(self, key: str) -> Any: + """Get attribute by key.""" + try: + return self[key] + except KeyError: + raise AttributeError(key) from None + + def __setattr__(self, key: str, value: Any) -> None: + """Set attribute by key.""" + if isinstance(value, MutableMapping): + value = Example(value) + self[key] = value + + def __setitem__(self, key: Hashable, value: Any) -> None: + """Set item by key.""" + if isinstance(value, MutableMapping): + value = Example(value) + super().__setitem__(key, value) + + +def _merge_examples(examples: list[Example]) -> dict[str, Any]: + """Convert a list of `dataset.example.Example` objects into a dictionary. + + This method will merge examples with the same key into a list. + + Parameters + ---------- + examples : list[Example] + List of examples to convert. + + Returns + ------- + dict[str, Any] + Dictionary of converted examples. + """ + merged_examples: dict[str, Any] = {} + for example in examples: + for key in example: + if key in merged_examples: + merged_examples[key].append(example[key]) + else: + merged_examples[key] = [example[key]] + + for key in merged_examples: + if isinstance(merged_examples[key][0], Example): + merged_examples[key] = _merge_examples(merged_examples[key]) + + return merged_examples + + +def _collate_example_dict(examples: dict[str, Any]) -> dict[str, Any]: + """Collate a dictionary of examples into a batch. + + Parameters + ---------- + examples : dict[str, Any] + Dictionary of examples to collate. + + Returns + ------- + dict[str, Any] + Dictionary of collated examples. + """ + batch = {} + for k, v in examples.items(): + if isinstance(v, dict): + batch[k] = _collate_example_dict(v) + else: + batch[k] = default_collate(v) + + return batch + + +def collate_example_list(examples: list[Example]) -> dict[str, Any]: + """Collate a list of `Example` objects into a batch. + + Parameters + ---------- + examples : list[Example] + List of examples to collate. + + Returns + ------- + dict[str, Any] + Dictionary of batched examples. + + """ + return _collate_example_dict(_merge_examples(examples)) + + +def find_matching_indices( + first_example_ids: torch.Tensor, + second_example_ids: torch.Tensor, +) -> tuple[torch.Tensor, torch.Tensor]: + """Find the indices of matching examples given two tensors of example ids. + + Matching examples are defined as examples with the same value in both tensors. + This method is useful for finding pairs of examples from different modalities + that are related to each other in a batch. + + Parameters + ---------- + first_example_ids : torch.Tensor + A tensor of example ids of shape `(N, 2)`, where `N` is the number of examples. + second_example_ids : torch.Tensor + A tensor of example ids of shape `(M, 2)`, where `M` is the number of examples. + + Returns + ------- + tuple[torch.Tensor, torch.Tensor] + A tuple of tensors containing the indices of matching examples in the first and + second tensor, respectively. + + Raises + ------ + TypeError + If either `first_example_ids` or `second_example_ids` is not a tensor. + ValueError + If either `first_example_ids` or `second_example_ids` is not a 2D tensor + with the second dimension having a size of `2`. + + Examples + -------- + >>> img_example_ids = torch.tensor([(0, 0), (0, 1), (1, 0), (1, 1)]) + >>> text_example_ids = torch.tensor([(1, 0), (1, 1), (2, 0), (2, 1), (2, 2)]) + >>> find_matching_indices(img_example_ids, text_example_ids) + (tensor([2, 3]), tensor([0, 1])) + + + """ + if not isinstance(first_example_ids, torch.Tensor) or not isinstance( + second_example_ids, + torch.Tensor, + ): + raise TypeError( + f"Expected inputs to be tensors, but got {type(first_example_ids)} " + f"and {type(second_example_ids)}.", + ) + val = 2 + if not (first_example_ids.ndim == val and first_example_ids.shape[1] == val): + raise ValueError( + "Expected argument `first_example_ids` to be a tensor of shape (N, 2), " + f"but got shape {first_example_ids.shape}.", + ) + if not (second_example_ids.ndim == val and second_example_ids.shape[1] == val): + raise ValueError( + "Expected argument `second_example_ids` to be a tensor of shape (N, 2), " + f"but got shape {second_example_ids.shape}.", + ) + + first_example_ids = first_example_ids.unsqueeze(1) # shape=(N, 1, 2) + second_example_ids = second_example_ids.unsqueeze(0) # shape=(1, M, 2) + + # compare all elements; results in a shape (N, M) tensor + matches = torch.all(first_example_ids == second_example_ids, dim=-1) + first_indices, second_indices = torch.where(matches) + return first_indices, second_indices diff --git a/mmlearn/datasets/core/modalities.py b/mmlearn/datasets/core/modalities.py new file mode 100644 index 0000000..e253749 --- /dev/null +++ b/mmlearn/datasets/core/modalities.py @@ -0,0 +1,290 @@ +"""Module for managing supported modalities in the library.""" + +import re +from typing import TYPE_CHECKING, Any, Optional + +from typing_extensions import Self + + +_default_supported_modalities = ["rgb", "depth", "thermal", "text", "audio", "video"] + + +class Modality(str): + """Class to represent a modality in the library. + + This class is used to represent a modality in the library. It contains the name of + the modality and the properties that can be associated with it. The properties are + dynamically generated based on the name of the modality and can be accessed as + attributes of the class. + + Parameters + ---------- + name : str + The name of the modality. + modality_specific_properties : Optional[dict[str, str]], optional, default=None + Additional properties specific to the modality, by default None + + Attributes + ---------- + value : str + The name of the modality. + properties : dict[str, str] + The properties associated with the modality. By default, the properties are + `target`, `mask`, `embedding`, `masked_embedding`, and `ema_embedding`. + These default properties apply to all newly created modality types + automatically. Modality-specific properties can be added using the + `add_property` method or by passing them as a dictionary to the constructor. + """ + + _default_properties = { + "target": "{}_target", + "mask": "{}_mask", + "embedding": "{}_embedding", + "masked_embedding": "{}_masked_embedding", + "ema_embedding": "{}_ema_embedding", + } + + if TYPE_CHECKING: + + def __getattr__(self, attr: str) -> Any: + """Get the value of the attribute.""" + ... + + def __setattr__(self, attr: str, value: Any) -> None: + """Set the value of the attribute.""" + ... + + def __new__( + cls, name: str, modality_specific_properties: Optional[dict[str, str]] = None + ) -> Self: + """Initialize the modality with the name and properties.""" + instance = super(Modality, cls).__new__(cls, name.lower()) + properties = cls._default_properties.copy() + if modality_specific_properties is not None: + properties.update(modality_specific_properties) + instance._properties = properties + + for property_name, format_string in instance._properties.items(): + instance._set_property_as_attr(property_name, format_string) + + return instance + + @property + def value(self) -> str: + """Return the name of the modality.""" + return self.__str__() + + @property + def properties(self) -> dict[str, str]: + """Return the properties associated with the modality.""" + return {name: getattr(self, name) for name in self._properties} + + def add_property(self, name: str, format_string: str) -> None: + """Add a new property to the modality. + + Parameters + ---------- + name : str + The name of the property. + format_string : str + The format string for the property. The format string should contain a + placeholder that will be replaced with the name of the modality when the + property is accessed. + + Raises + ------ + ValueError + If the property already exists for the modality or if the format string is + invalid. + """ + if name in self._properties: + raise ValueError( + f"Property '{name}' already exists for modality '{super().__str__()}'." + ) + self._properties[name] = format_string + self._set_property_as_attr(name, format_string) + + def _set_property_as_attr(self, name: str, format_string: str) -> None: + """Set the property as an attribute of the modality.""" + if not _is_format_string(format_string): + raise ValueError( + f"Invalid format string '{format_string}' for property " + f"'{name}' of modality '{super().__str__()}'." + ) + setattr(self, name, format_string.format(self.value)) + + def __str__(self) -> str: + """Return the object as a string.""" + return self.lower() + + def __repr__(self) -> str: + """Return the string representation of the modality.""" + return f"" + + def __hash__(self) -> int: + """Return the hash of the modality name and properties.""" + return hash((self.value, tuple(self._properties.items()))) + + def __eq__(self, other: object) -> bool: + """Check if two modality types are equal. + + Two modality types are equal if they have the same name and properties. + """ + return isinstance(other, Modality) and ( + (self.__str__() == other.__str__()) + and (self._properties == other._properties) + ) + + +class ModalityRegistry: + """Modality registry. + + A singleton class that manages the supported modalities (and their properties) in + the library. The class provides methods to add new modalities and properties, and + to access the existing modalities. The class is implemented as a singleton to + ensure that there is only one instance of the registry in the library. + """ + + _instance = None + + def __new__(cls) -> Self: + """Create a new instance of the class if it does not exist.""" + if cls._instance is None: + cls._instance = super(ModalityRegistry, cls).__new__(cls) + cls._instance._modality_registry = {} # type: ignore[attr-defined] + for modality in _default_supported_modalities: + cls._instance.register_modality(modality) + return cls._instance + + def register_modality( + self, name: str, modality_specific_properties: Optional[dict[str, str]] = None + ) -> None: + """Add a new modality to the registry. + + Parameters + ---------- + name : str + The name of the modality. + modality_specific_properties : Optional[dict[str, str]], optional, default=None + Additional properties specific to the modality. + + Raises + ------ + ValueError + If the modality already exists in the registry. + """ + if name.lower() in self._modality_registry: + raise ValueError(f"Modality '{name}' already exists in the registry.") + + name = name.lower() + modality = Modality(name, modality_specific_properties) + self._modality_registry[name] = modality + setattr(self, name, modality) + + def add_default_property(self, name: str, format_string: str) -> None: + """Add a new property that is applicable to all modalities. + + Parameters + ---------- + name : str + The name of the property. + format_string : str + The format string for the property. The format string should contain a + placeholder that will be replaced with the name of the modality when the + property is accessed. + + Raises + ------ + ValueError + If the property already exists for the default properties or if the format + string is invalid. + """ + for modality in self._modality_registry.values(): + modality.add_property(name, format_string) + + # add the property to the default properties for new modalities + Modality._default_properties[name.lower()] = format_string + + def has_modality(self, name: str) -> bool: + """Check if the modality exists in the registry. + + Parameters + ---------- + name : str + The name of the modality. + + Returns + ------- + bool + True if the modality exists in the registry, False otherwise. + """ + return name.lower() in self._modality_registry + + def get_modality(self, name: str) -> Modality: + """Get the modality name from the registry. + + Parameters + ---------- + name : str + The name of the modality. + + Returns + ------- + Modality + The modality object from the registry. + """ + return self._modality_registry[name.lower()] # type: ignore[index,return-value] + + def get_modality_properties(self, name: str) -> dict[str, str]: + """Get the properties of a modality from the registry. + + Parameters + ---------- + name : str + The name of the modality. + + Returns + ------- + dict[str, str] + The properties associated with the modality. + """ + return self.get_modality(name).properties + + def list_modalities(self) -> list[Modality]: + """Get the list of supported modalities in the registry. + + Returns + ------- + list[Modality] + The list of supported modalities in the registry. + """ + return list(self._modality_registry.values()) + + def __getattr__(self, name: str) -> Modality: + """Access a modality as an attribute by its name.""" + if name.lower() in self._modality_registry: + return self._modality_registry[name.lower()] # type: ignore[index,return-value] + raise AttributeError( + f"'{self.__class__.__name__}' object has no attribute '{name}'" + ) + + +def _is_format_string(string: str) -> bool: + """Check if the string is a format string. + + A format string is a string that contains one or more placeholders enclosed in + curly braces. This function checks if the string contains at least one placeholder + and returns a boolean value indicating whether it is a format string. + + Parameters + ---------- + string : str + The string to check. + + Returns + ------- + bool + True if the string is a format string, False otherwise. + """ + pattern = r"\{.*?\}" + return bool(re.search(pattern, string)) diff --git a/mmlearn/datasets/core/samplers.py b/mmlearn/datasets/core/samplers.py new file mode 100644 index 0000000..9ccf97e --- /dev/null +++ b/mmlearn/datasets/core/samplers.py @@ -0,0 +1,394 @@ +"""Samplers for data loading.""" + +import math +from typing import Iterator, Optional, Sequence, Sized + +import torch +import torch.distributed as dist +from hydra_zen import store +from torch.utils.data import Dataset, Sampler + +from mmlearn.datasets.core.combined_dataset import CombinedDataset + + +@store(group="dataloader/sampler", provider="mmlearn") +class CombinedDatasetRatioSampler(Sampler[int]): + """Sampler for weighted sampling from a `CombinedDataset`. + + Parameters + ---------- + dataset : CombinedDataset + An instance of `CombinedDataset` to sample from. + ratios : Sequence[float], optional, default=None + A sequence of ratios for sampling from each dataset in the combined dataset. + The length of the sequence must be equal to the number of datasets in the + combined dataset (`dataset`). If `None`, the length of each dataset in the + combined dataset is used as the ratio. The ratios are normalized to sum to 1. + num_samples : int, optional, default=None + The number of samples to draw from the combined dataset. If `None`, the + sampler will draw as many samples as there are in the combined dataset. + This number must yield at least one sample per dataset in the combined + dataset, when multiplied by the corresponding ratio. + replacement : bool, default=False + Whether to sample with replacement or not. + shuffle : bool, default=True + Whether to shuffle the sampled indices or not. If `False`, the indices of + each dataset will appear in the order they are stored in the combined dataset. + This is similar to sequential sampling from each dataset. The datasets + that make up the combined dataset are still sampled randomly. + rank : int, optional, default=None + Rank of the current process within :attr:`num_replicas`. By default, + :attr:`rank` is retrieved from the current distributed group. + num_replicas : int, optional, default=None + Number of processes participating in distributed training. By + default, :attr:`num_replicas` is retrieved from the current distributed group. + drop_last : bool, default=False + Whether to drop the last incomplete batch or not. If `True`, the sampler will + drop samples to make the number of samples evenly divisible by the number of + replicas in distributed mode. + seed : int, default=0 + Random seed used to when sampling from the combined dataset and shuffling + the sampled indices. + + Attributes + ---------- + dataset : CombinedDataset + The dataset to sample from. + num_samples : int + The number of samples to draw from the combined dataset. + probs : torch.Tensor + The probabilities for sampling from each dataset in the combined dataset. + This is computed from the `ratios` argument and is normalized to sum to 1. + replacement : bool + Whether to sample with replacement or not. + shuffle : bool + Whether to shuffle the sampled indices or not. + rank : int + Rank of the current process within :attr:`num_replicas`. + num_replicas : int + Number of processes participating in distributed training. + drop_last : bool + Whether to drop samples to make the number of samples evenly divisible by the + number of replicas in distributed mode. + seed : int + Random seed used to when sampling from the combined dataset and shuffling + the sampled indices. + epoch : int + Current epoch number. This is used to set the random seed. This is useful + in distributed mode to ensure that each process receives a different random + ordering of the samples. + total_size : int + The total number of samples across all processes. + """ + + def __init__( # noqa: PLR0912 + self, + dataset: CombinedDataset, + ratios: Optional[Sequence[float]] = None, + num_samples: Optional[int] = None, + replacement: bool = False, + shuffle: bool = True, + rank: Optional[int] = None, + num_replicas: Optional[int] = None, + drop_last: bool = False, + seed: int = 0, + ): + """Initialize the sampler.""" + if not isinstance(dataset, CombinedDataset): + raise TypeError( + "Expected argument `dataset` to be of type `CombinedDataset`, " + f"but got {type(dataset)}.", + ) + if not isinstance(seed, int): + raise TypeError( + f"Expected argument `seed` to be an integer, but got {type(seed)}.", + ) + if num_replicas is None: + if not dist.is_available(): + raise RuntimeError("Requires distributed package to be available") + num_replicas = dist.get_world_size() + if rank is None: + if not dist.is_available(): + raise RuntimeError("Requires distributed package to be available") + rank = dist.get_rank() + if rank >= num_replicas or rank < 0: + raise ValueError( + f"Invalid rank {rank}, rank should be in the interval [0, {num_replicas - 1}]" + ) + + self.dataset = dataset + self.num_replicas = num_replicas + self.rank = rank + self.drop_last = drop_last + self.replacement = replacement + self.shuffle = shuffle + self.seed = seed + self.epoch = 0 + + self._num_samples = num_samples + if not isinstance(self.num_samples, int) or self.num_samples <= 0: + raise ValueError( + "Expected argument `num_samples` to be a positive integer, but got " + f"{self.num_samples}.", + ) + + if ratios is None: + ratios = [len(subset) for subset in self.dataset.datasets] + + num_datasets = len(self.dataset.datasets) + if len(ratios) != num_datasets: + raise ValueError( + f"Expected argument `ratios` to be of length {num_datasets}, " + f"but got length {len(ratios)}.", + ) + prob_sum = sum(ratios) + if not all(ratio >= 0 for ratio in ratios) and prob_sum > 0: + raise ValueError( + "Expected argument `ratios` to be a sequence of non-negative numbers. " + f"Got {ratios}.", + ) + self.probs = torch.tensor( + [ratio / prob_sum for ratio in ratios], + dtype=torch.double, + ) + if any((prob * self.num_samples) <= 0 for prob in self.probs): + raise ValueError( + "Expected dataset ratio to result in at least one sample per dataset. " + f"Got dataset sizes {self.probs * self.num_samples}.", + ) + + @property + def num_samples(self) -> int: + """Return the number of samples managed by the sampler.""" + # dataset size might change at runtime + if self._num_samples is None: + num_samples = len(self.dataset) + else: + num_samples = self._num_samples + + if self.drop_last and num_samples % self.num_replicas != 0: + # split to nearest available length that is evenly divisible. + # This is to ensure each rank receives the same amount of data when + # using this Sampler. + num_samples = math.ceil( + (num_samples - self.num_replicas) / self.num_replicas, + ) + else: + num_samples = math.ceil(num_samples / self.num_replicas) + return num_samples + + @property + def total_size(self) -> int: + """Return the total size of the dataset.""" + return self.num_samples * self.num_replicas + + def __iter__(self) -> Iterator[int]: + """Return an iterator that yields sample indices for the combined dataset.""" + generator = torch.Generator() + seed = self.seed + self.epoch + generator.manual_seed(seed) + + cumulative_sizes = [0] + self.dataset.cumulative_sizes + num_samples_per_dataset = [int(prob * self.total_size) for prob in self.probs] + indices = [] + for i in range(len(self.dataset.datasets)): + per_dataset_indices: torch.Tensor = torch.multinomial( + torch.ones(cumulative_sizes[i + 1] - cumulative_sizes[i]), + num_samples_per_dataset[i], + replacement=self.replacement, + generator=generator, + ) + # adjust indices to reflect position in cumulative dataset + per_dataset_indices += cumulative_sizes[i] + assert per_dataset_indices.max() < cumulative_sizes[i + 1], ( + f"Indices from dataset {i} exceed dataset size. " + f"Got indices {per_dataset_indices} and dataset size {cumulative_sizes[i + 1]}.", + ) + indices.append(per_dataset_indices) + + indices = torch.cat(indices) + if self.shuffle: + rand_indices = torch.randperm(len(indices), generator=generator) + indices = indices[rand_indices] + + indices = indices.tolist() # type: ignore[attr-defined] + num_indices = len(indices) + + if num_indices < self.total_size: + padding_size = self.total_size - num_indices + if padding_size <= num_indices: + indices += indices[:padding_size] + else: + indices += (indices * math.ceil(padding_size / num_indices))[ + :padding_size + ] + elif num_indices > self.total_size: + indices = indices[: self.total_size] + assert len(indices) == self.total_size + + # subsample + indices = indices[self.rank : self.total_size : self.num_replicas] + assert len(indices) == self.num_samples, ( + f"Expected {self.num_samples} samples, but got {len(indices)}.", + ) + + yield from iter(indices) + + def __len__(self) -> int: + """Return the total number of samples in the sampler.""" + return self.num_samples + + def set_epoch(self, epoch: int) -> None: + """Set the epoch for this sampler. + + When :attr:`shuffle=True`, this ensures all replicas use a different random + ordering for each epoch. Otherwise, the next iteration of this sampler + will yield the same ordering. + + Parameters + ---------- + epoch : int + Epoch number. + + """ + self.epoch = epoch + + # some iterable datasets (especially huggingface iterable datasets) might + # require setting the epoch to ensure shuffling works properly + for dataset in self.dataset.datasets: + if hasattr(dataset, "set_epoch"): + dataset.set_epoch(epoch) + + +@store(group="dataloader/sampler", provider="mmlearn") +class DistributedEvalSampler(Sampler[int]): + """Sampler for distributed evaluation. + + Adapted from: https://github.com/SeungjunNah/DeepDeblur-PyTorch/blob/master/src/data/sampler.py + See also: https://discuss.pytorch.org/t/how-to-validate-in-distributeddataparallel-correctly/94267/11 + + DistributedEvalSampler is different from DistributedSampler. It does NOT add + extra samples to make it evenly divisible. + + DistributedEvalSampler should NOT be used for training. The distributed processes + could hang forever. + See this issue for details: https://github.com/pytorch/pytorch/issues/22584 + + Shuffle is disabled by default. + + DistributedEvalSampler is for evaluation purpose where synchronization does + not happen every epoch. Synchronization should be done outside the dataloader loop. + Sampler that restricts data loading to a subset of the dataset. It is especially + useful in conjunction with `torch.nn.parallel.DistributedDataParallel`. + + Dataset is assumed to be of constant size. + + Parameters + ---------- + dataset : torch.utils.data.Dataset + Dataset used for sampling. + num_replicas : int, optional, default=`None` + Number of processes participating in distributed training. By + default, :attr:`rank` is retrieved from the current distributed group. + rank : int, optional, default=`None` + Rank of the current process within :attr:`num_replicas`. By default, + :attr:`rank` is retrieved from the current distributed group. + shuffle : bool, optional, default=`False` + If `True` (default), sampler will shuffle the indices. + seed : int, optional, default=0 + Random seed used to shuffle the sampler if :attr:`shuffle=True`. + This number should be identical across all processes in the + distributed group. + + Warnings + -------- + In distributed mode, calling the :meth`set_epoch(epoch) ` + method at the beginning of each epoch **before** creating the + `torch.utils.data.DataLoader` iterator is necessary to make shuffling work properly + across multiple epochs. Otherwise, the same ordering will be always used. + + + Examples + -------- + >>> def example(): + ... start_epoch, n_epochs = 0, 2 + ... sampler = DistributedEvalSampler(dataset) if is_distributed else None + ... loader = DataLoader(dataset, shuffle=(sampler is None), sampler=sampler) + ... for epoch in range(start_epoch, n_epochs): + ... if is_distributed: + ... sampler.set_epoch(epoch) + ... evaluate(loader) + + """ + + def __init__( + self, + dataset: Dataset[Sized], + num_replicas: Optional[int] = None, + rank: Optional[int] = None, + shuffle: bool = False, + seed: int = 0, + ) -> None: + """Initialize the sampler.""" + if num_replicas is None: + if not dist.is_available(): + raise RuntimeError("Requires distributed package to be available") + num_replicas = dist.get_world_size() + if rank is None: + if not dist.is_available(): + raise RuntimeError("Requires distributed package to be available") + rank = dist.get_rank() + self.dataset = dataset + self.num_replicas = num_replicas + self.rank = rank + self.epoch = 0 + self.shuffle = shuffle + self.seed = seed + + @property + def total_size(self) -> int: + """Return the total size of the dataset.""" + return len(self.dataset) + + @property + def num_samples(self) -> int: + """Return the number of samples managed by the sampler.""" + indices = list(range(self.total_size))[ + self.rank : self.total_size : self.num_replicas + ] + return len(indices) # true value without extra samples + + def __iter__(self) -> Iterator[int]: + """Return an iterator that iterates over the indices of the dataset.""" + if self.shuffle: + # deterministically shuffle based on epoch and seed + g = torch.Generator() + g.manual_seed(self.seed + self.epoch) + indices = torch.randperm(self.total_size, generator=g).tolist() + else: + indices = list(range(self.total_size)) + + # subsample + indices = indices[self.rank : self.total_size : self.num_replicas] + assert len(indices) == self.num_samples + + return iter(indices) + + def __len__(self) -> int: + """Return the number of samples.""" + return self.num_samples + + def set_epoch(self, epoch: int) -> None: + """Set the epoch for this sampler. + + When :attr:`shuffle=True`, this ensures all replicas use a different random + ordering for each epoch. Otherwise, the next iteration of this sampler + will yield the same ordering. + + Parameters + ---------- + epoch : int + Epoch number. + + """ + self.epoch = epoch diff --git a/mmlearn/datasets/ego4d.py b/mmlearn/datasets/ego4d.py new file mode 100644 index 0000000..896f741 --- /dev/null +++ b/mmlearn/datasets/ego4d.py @@ -0,0 +1,126 @@ +"""Ego4D dataset.""" + +import glob +import os +from typing import Any, Callable, List, Optional, Tuple + +import torch +from hydra_zen import MISSING, store +from pytorchvideo import transforms as pv_transforms +from pytorchvideo.data.clip_sampling import ConstantClipsPerVideoSampler +from pytorchvideo.data.encoded_video import EncodedVideo +from torch.utils.data import Dataset +from torchvision import transforms + +from mmlearn.constants import EXAMPLE_INDEX_KEY +from mmlearn.datasets.core import Modalities +from mmlearn.datasets.core.example import Example + + +@store( + name="Ego4D", + group="datasets", + provider="mmlearn", + root_dir=os.getenv("EGO4D_ROOT_DIR", MISSING), +) +class Ego4DDataset(Dataset[Example]): + """A PyTorch Dataset for loading and processing videos from the Ego4D dataset. + + Parameters + ---------- + root_dir : List[str] + Path to the root directory containing the video files. + clip_duration : int + Duration of each video clip in seconds. + clips_per_video : int + Number of clips to sample from each video. + sample_rate : int + Sample rate for audio processing. + video_transform : Optional[Callable], default=None + A callable that takes in a video clip and returns a transformed version of it. + """ + + def __init__( + self, + root_dir: str, + clip_duration: int = 2, + clips_per_video: int = 5, + sample_rate: int = 16000, + video_transform: Optional[Callable[..., Any]] = None, + ) -> None: + """Initialize the dataset.""" + self.video_paths = glob.glob(os.path.join(root_dir, "*.mp4")) + self.clip_duration = clip_duration + self.clips_per_video = clips_per_video + self.sample_rate = sample_rate + + if video_transform is not None: + self.video_transform = video_transform + else: + self.video_transform = transforms.Compose( + [ + pv_transforms.ShortSideScale(224), + pv_transforms.Normalize( + mean=(0.48145466, 0.4578275, 0.40821073), + std=(0.26862954, 0.26130258, 0.27577711), + ), + ], + ) + + def __len__(self) -> int: + """Return the length of the dataset.""" + return len(self.video_paths) + + def __getitem__(self, idx: int) -> Example: + """Return a video clip from the dataset.""" + video_path = self.video_paths[idx] + video = EncodedVideo.from_path(video_path, decoder="decord", decode_audio=False) + + clip_sampler = ConstantClipsPerVideoSampler( + clip_duration=self.clip_duration, + clips_per_video=self.clips_per_video, + ) + all_clips_timepoints = self._get_clip_timepoints(clip_sampler, video.duration) + + all_video = [] + for start, end in all_clips_timepoints: + clip = video.get_clip(start, end) + if clip is None: + raise ValueError("No clip found") + video_clip = clip["video"] / 255.0 # Normalizing + video_clip = self.video_transform(video_clip) + all_video.append(video_clip) + + all_video = torch.stack(all_video, dim=0) + return Example({Modalities.VIDEO: all_video, EXAMPLE_INDEX_KEY: idx}) + + def _get_clip_timepoints( + self, + clip_sampler: ConstantClipsPerVideoSampler, + duration: float, + ) -> List[Tuple[float, float]]: + """Calculate the start and end timepoints for each video clip. + + Parameters + ---------- + clip_sampler + The clip sampler instance. + duration : int + Total duration of the video. + + Returns + ------- + list of tuples + List of tuples containing start and end timepoints of each clip. + """ + all_clips_timepoints = [] + is_last_clip = False + end = 0.0 + while not is_last_clip: + start, end, _, _, is_last_clip = clip_sampler( + end, + duration, + annotation=None, + ) + all_clips_timepoints.append((start, end)) + return all_clips_timepoints diff --git a/mmlearn/datasets/imagenet.py b/mmlearn/datasets/imagenet.py new file mode 100644 index 0000000..cdecf93 --- /dev/null +++ b/mmlearn/datasets/imagenet.py @@ -0,0 +1,69 @@ +"""ImageNet dataset.""" + +import os +from typing import Any, Callable, Literal, Optional + +from hydra_zen import MISSING, store +from torchvision.datasets.folder import ImageFolder + +from mmlearn.constants import EXAMPLE_INDEX_KEY +from mmlearn.datasets.core import Modalities +from mmlearn.datasets.core.example import Example + + +@store( + group="datasets", + provider="mmlearn", + root_dir=os.getenv("IMAGENET_ROOT_DIR", MISSING), +) +class ImageNet(ImageFolder): + """ImageNet dataset. + + This is a wrapper around `torchvision.datasets.ImageFolder` that returns a + `dataset.example.Example` object. + + Parameters + ---------- + root_dir : str + Path to the root directory of the dataset. + split : {"train", "val"}, default="train" + The split of the dataset to use. + transform : Callable, optional, default=None + A function/transform that takes in an PIL image and returns a transformed + version. + target_transform : Callable, optional, default=None + A function/transform that takes in the target and transforms it. + mask_generator : Optional[Callable[..., Any]], optional, default=None + Generator for the mask. + """ + + def __init__( + self, + root_dir: str, + split: Literal["train", "val"] = "train", + transform: Optional[Callable[..., Any]] = None, + target_transform: Optional[Callable[..., Any]] = None, + mask_generator: Optional[Callable[..., Any]] = None, + ) -> None: + """Initialize the dataset.""" + split = "train" if split == "train" else "val" + root_dir = os.path.join(root_dir, split) + super().__init__( + root=root_dir, transform=transform, target_transform=target_transform + ) + self.mask_generator = mask_generator + + def __getitem__(self, index: int) -> Example: + """Get an example at the given index.""" + image, target = super().__getitem__(index) + example = Example( + { + Modalities.RGB: image, + Modalities.RGB.target: target, + EXAMPLE_INDEX_KEY: index, + } + ) + mask = self.mask_generator() if self.mask_generator else None + if mask is not None: # error will be raised during collation if `None` + example[Modalities.RGB.mask] = mask + return example diff --git a/mmlearn/datasets/librispeech.py b/mmlearn/datasets/librispeech.py new file mode 100644 index 0000000..40468ab --- /dev/null +++ b/mmlearn/datasets/librispeech.py @@ -0,0 +1,108 @@ +"""LibriSpeech dataset.""" + +import os + +import torch +import torch.nn.functional as F # noqa: N812 +from hydra_zen import MISSING, store +from torch.utils.data.dataset import Dataset +from torchaudio.datasets import LIBRISPEECH + +from mmlearn.constants import EXAMPLE_INDEX_KEY +from mmlearn.datasets.core import Modalities +from mmlearn.datasets.core.example import Example + + +SAMPLE_RATE = 16000 + + +def pad_or_trim( + array: torch.Tensor, + length: int = 30 * SAMPLE_RATE, + *, + axis: int = -1, +) -> torch.Tensor: + """Pad or trim the audio array to `length` along the given axis. + + Adapted from: https://github.com/openai/whisper/blob/main/whisper/audio.py#L65C1-L88C17 + + Parameters + ---------- + array : torch.Tensor + Audio array. + length : int, default=480000 + Length to pad or trim to. Defaults to 30 seconds at 16 kHz. + axis : int, default=-1 + Axis along which to pad or trim. + + Returns + ------- + array : torch.Tensor + Padded or trimmed audio array. + """ + if array.shape[axis] > length: + array = array.index_select( + dim=axis, + index=torch.arange(length, device=array.device), + ) + + if array.shape[axis] < length: + pad_widths = [(0, 0)] * array.ndim + pad_widths[axis] = (0, length - array.shape[axis]) + array = F.pad(array, [pad for sizes in pad_widths[::-1] for pad in sizes]) + + return array + + +@store( + group="datasets", + provider="mmlearn", + root_dir=os.getenv("LIBRISPEECH_ROOT_DIR", MISSING), +) +class LibriSpeech(Dataset[Example]): + """LibriSpeech dataset. + + This is a wrapper around `torchaudio.datasets.LIBRISPEECH` that assumes that + the dataset is already downloaded and the top-level directory of the dataset + in the root directory is `librispeech`. + This class only returns the audio and transcript from the dataset. + + Parameters + ---------- + root_dir : str + Root directory of dataset. + split : {"train-clean-100", "train-clean-360", "train-other-500", "dev-clean", + "dev-other", "test-clean", "test-other"}, default="train-clean-100" + Split of the dataset to use. + + """ + + def __init__(self, root_dir: str, split: str = "train-clean-100") -> None: + """Initialize LibriSpeech dataset.""" + super().__init__() + self.dataset = LIBRISPEECH( + root=root_dir, + url=split, + download=False, + folder_in_archive="librispeech", + ) + + def __len__(self) -> int: + """Return the length of the dataset.""" + return len(self.dataset) + + def __getitem__(self, idx: int) -> Example: + """Return an example from the dataset.""" + waveform, sample_rate, transcript, _, _, _ = self.dataset[idx] + assert ( + sample_rate == SAMPLE_RATE + ), f"Expected sample rate to be `16000`, got {sample_rate}." + waveform = pad_or_trim(waveform.flatten()) + + return Example( + { + Modalities.AUDIO: waveform, + Modalities.TEXT: transcript, + EXAMPLE_INDEX_KEY: idx, + }, + ) diff --git a/mmlearn/datasets/llvip.py b/mmlearn/datasets/llvip.py new file mode 100644 index 0000000..94867df --- /dev/null +++ b/mmlearn/datasets/llvip.py @@ -0,0 +1,128 @@ +"""LLVIP dataset.""" + +import glob +import os +import xml.etree.ElementTree as ET # noqa: N817 +from typing import Callable, Dict, Optional + +import numpy as np +import torch +from hydra_zen import MISSING, store +from PIL.Image import Image as PILImage +from torch.utils.data import Dataset +from torchvision import transforms + +from mmlearn.constants import EXAMPLE_INDEX_KEY +from mmlearn.datasets.core import Modalities +from mmlearn.datasets.core.example import Example + + +@store( + name="LLVIP", + group="datasets", + provider="mmlearn", + root_dir=os.getenv("LLVIP_ROOT_DIR", MISSING), +) +class LLVIPDataset(Dataset[Example]): + """A dataset class for the LLVIP dataset which handles RGB and IR images. + + Parameters + ---------- + root_dir : str + Path to the root directory of the dataset. The directory should contain + 'visible' and 'infrared' subdirectories. + train : bool, default=True + Flag to indicate training or testing phase. + transform : Optional[Callable], optional, default=None + Transformations to be applied to the images. + """ + + def __init__( + self, + root_dir: str, + train: bool = True, + transform: Optional[Callable[[PILImage], torch.Tensor]] = None, + ): + """Initialize the dataset.""" + self.path_images_rgb = os.path.join( + root_dir, + "visible", + "train" if train else "test", + ) + self.path_images_ir = os.path.join( + root_dir, "infrared", "train" if train else "test" + ) + self.train = train + self.transform = transform or transforms.ToTensor() + + self.rgb_images = sorted(glob.glob(os.path.join(self.path_images_rgb, "*.jpg"))) + self.ir_images = sorted(glob.glob(os.path.join(self.path_images_ir, "*.jpg"))) + + def __len__(self) -> int: + """Return the length of the dataset.""" + return len(self.rgb_images) + + def __getitem__(self, idx: int) -> Example: + """Return an example from the dataset.""" + rgb_image_path = self.rgb_images[idx] + ir_image_path = self.ir_images[idx] + + rgb_image = PILImage.open(rgb_image_path).convert("RGB") + ir_image = PILImage.open(ir_image_path).convert("L") + + sample = Example( + { + Modalities.RGB: self.transform(rgb_image), + Modalities.THERMAL: self.transform(ir_image), + EXAMPLE_INDEX_KEY: idx, + }, + ) + + if self.train: + annot_path = ( + rgb_image_path.replace("visible", "Annotations") + .replace(".jpg", ".xml") + .replace("train", "") + ) + annot = self._get_bbox(annot_path) + sample["annotation"] = { + "bboxes": torch.from_numpy(annot["bboxes"]), + "labels": torch.from_numpy(annot["labels"]), + } + return sample + + def _get_bbox(self, filename: str) -> Dict[str, np.ndarray]: + """Parse the XML file to get bounding boxes and labels. + + Parameters + ---------- + filename : str + Path to the annotation XML file. + + Returns + ------- + dict + A dictionary containing bounding boxes and labels. + """ + try: + root = ET.parse(filename).getroot() + + bboxes, labels = [], [] + for obj in root.findall("object"): + bbox_obj = obj.find("bndbox") + bbox = [ + int(bbox_obj.find(dim).text) # type: ignore[union-attr,arg-type] + for dim in ["xmin", "ymin", "xmax", "ymax"] + ] + bboxes.append(bbox) + labels.append(1) # Assuming 'person' is the only label + return { + "bboxes": np.array(bboxes).astype("float"), + "labels": np.array(labels).astype("int"), + } + except ET.ParseError as e: + raise ValueError(f"Error parsing XML: {e}") from None + except Exception as e: + raise RuntimeError( + f"Error processing annotation file {filename}: {e}", + ) from None diff --git a/mmlearn/datasets/medvqa.py b/mmlearn/datasets/medvqa.py new file mode 100644 index 0000000..60192a1 --- /dev/null +++ b/mmlearn/datasets/medvqa.py @@ -0,0 +1,236 @@ +"""PathVQA and VQARAD datasets for medical visual question answering.""" + +import json +import os +import warnings +from typing import Any, Callable, Dict, Literal, Optional + +import numpy as np +import torch +from hydra_zen import MISSING, builds, store +from PIL import Image +from torch.utils.data import Dataset +from torchvision.transforms import CenterCrop, Compose, Grayscale, Resize, ToTensor + +from mmlearn.constants import EXAMPLE_INDEX_KEY +from mmlearn.datasets.core import Modalities +from mmlearn.datasets.core.example import Example + + +with warnings.catch_warnings(): + warnings.filterwarnings("ignore", category=FutureWarning) + + +class MedVQA(Dataset[Example]): + """Module to load PathVQA and VQARAD datasets. + + Parameters + ---------- + root_dir : str + Path to the root directory of the dataset. + split : {"train", "val", "test"}, default = "train" + Split of the dataset to use. + encoder : dict + images_filename : str + Path to .pkl file containing the encoder's input images, + relative to root_dir. + image_size : int + Size of the input images; e.g. 224 for clipvision + feat_dim : int + Dimension of the output embedding; e.g. 512 for clipvision + autoencoder : dict + available : boolean {True, False} + Whether or not to return autoencoder images. + images_filename : str + Path to .pkl file containing the autoencoder's input images, + relative to root_dir. + image_size : int + Size of the input images; e.g. 128 + feat_dim : str + Dimension of the output embedding; e.g. 64 + num_ans_candidates : int + Number of all unique answers in the dataset. + rgb_transform : Optional[Callable[[Image.Image], torch.Tensor]] + Transform applied to images that will be passed to the visual encoder. + ae_transform : Optional[Callable[[Image.Image], torch.Tensor]] + Transform applied to images that will be passed to the autoencoder. + tokenizer : Optional[torch.nn.Module] + Function to tokenize the questions. + """ + + def __init__( + self, + root_dir: str, + split: Literal["train", "val", "test"], + encoder: Dict[str, Any], + autoencoder: Dict[str, Any], + num_ans_candidates: int, + rgb_transform: Optional[Callable[[Image.Image], torch.Tensor]] = None, + ae_transform: Optional[Callable[[Image.Image], torch.Tensor]] = None, + tokenizer: Optional[torch.nn.Module] = None, + ) -> None: + """Initialize the dataset.""" + super(MedVQA, self).__init__() + assert split in ["train", "val", "test"] + self.autoencoder = autoencoder["available"] + self.num_ans_candidates = num_ans_candidates + + # transform for encoder images + if rgb_transform is None: + self.rgb_transform = ToTensor() + else: + self.rgb_transform = rgb_transform + + # transform for autoencoder images + if self.autoencoder and ae_transform is None: + self.ae_transform = Compose( + [ + Grayscale(1), + Resize(autoencoder["image_size"]), + CenterCrop(autoencoder["image_size"]), + ToTensor(), + ] + ) + elif self.autoencoder: + self.ae_transform = ae_transform + + # tokenizer for textual questions + self.tokenize_fn = tokenizer + + # load entries + with open( + os.path.join(root_dir, "cache", f"{split}_data.json"), encoding="utf-8" + ) as file: + self.entries = json.load(file) + + if self.autoencoder: + self.v_dim = encoder["feat_dim"] + autoencoder["feat_dim"] + else: + self.v_dim = encoder["feat_dim"] + + def __getitem__(self, index: int) -> Example: + """Return an example/sample of the data. + + Returns + ------- + Example : Example + One sample of the dataset. + Modalities.TEXT : torch.Tensor + Question as tokens. + Modalities.RGB : torch.Tensor | List[torch.Tensor] + Preprocessed image. + A list of two torch Tensors if `autoencoder.available` is set + True in the dataset config, otherwise a single torch Tensor. + Modalities.RGB.target : torch.Tensor + Multi-hot-encoding of the correct answer classes as a vector. + EXAMPLE_INDEX_KEY : int + Sample index. + "qid" : int + The qid of the sample. + "answer_type" : str {"yes/no", "number", "OPEN", "CLOSED", ...} + Answer type. + "question_type" : str {"what", "does", "are", "SIZE", "PRES", ...} + Question type. + "phrase_type" : str {"freeform", "frame"} | int {-1} + Phrase type. + (-1 in case the dataset does not have phrase_type info). + "raw_question" : str + Question as text. + + Notes + ----- + If `autoencoder.available` is set True in the dataset configs, a list + of two torch Tensors are returned as `Modalities.RGB`. The first element + of the list is the processed image meant for the visual encoder and + the second element is the image meant for the autoencoder in the MEVF + pipeline (see [1] for more information). If `autoencoder.available` is + False, only the image meant for the encoder is returned. + + References + ---------- + [1] Nguyen, Binh D., Thanh-Toan Do, Binh X. Nguyen, Tuong Do, Erman + Tjiputra, and Quang D. Tran. "Overcoming data limitation in medical + visual question answering." In Medical Image Computing and Computer + Assisted Intervention, MICCAI 2019: 22nd International Conference. + """ + entry = self.entries[index] + question = ( + self.tokenize_fn(entry["question"]) + if self.tokenize_fn + else entry["question"] + ) + answer = entry["answer"] + + # prepare encoder image + images_data = Image.open(entry["image_path"]).convert("RGB") + enc_images_data = self.rgb_transform(images_data) + + # prepare autoencoder image + if self.autoencoder: + ae_images_data = self.ae_transform(images_data) + + # pack images if needed + if self.autoencoder: + image_data = [enc_images_data, ae_images_data] + else: + image_data = enc_images_data + + example = Example( + { + Modalities.TEXT: question, + Modalities.RGB: image_data, + EXAMPLE_INDEX_KEY: index, + "qid": entry["qid"], + "answer_type": entry["answer_type"], + "question_type": entry["question_type"], + "phrase_type": entry["phrase_type"], + "raw_question": entry["question"], + }, + ) + + if answer is not None: + labels = answer["labels"] + scores = answer["scores"] + target = torch.zeros(self.num_ans_candidates) + if len(labels): + labels = torch.from_numpy(np.array(answer["labels"])) + scores = torch.from_numpy(np.array(answer["scores"], dtype=np.float32)) + target.scatter_(0, labels, scores) + example[Modalities.RGB.target] = target + + return example + + def __len__(self) -> int: + """Return size of the dataset.""" + return len(self.entries) + + +_MedVQAConf = builds( + MedVQA, + split="train", + encoder={"image_size": 224, "feat_dim": 512, "images_filename": "images_clip.pkl"}, + autoencoder={ + "available": True, + "image_size": 128, + "feat_dim": 64, + "images_filename": "images128x128.pkl", + }, + num_ans_candidates=MISSING, +) +_PathVQAConf = builds( + MedVQA, + root_dir=os.getenv("PATHVQA_ROOT_DIR", MISSING), + num_ans_candidates=3974, + autoencoder={"available": False}, + builds_bases=(_MedVQAConf,), +) +_VQARADConf = builds( + MedVQA, + root_dir=os.getenv("VQARAD_ROOT_DIR", MISSING), + num_ans_candidates=458, + autoencoder={"available": False}, + builds_bases=(_MedVQAConf,), +) +store(_MedVQAConf, name="MedVQA", group="datasets", provider="mmlearn") +store(_PathVQAConf, name="PathVQA", group="datasets", provider="mmlearn") +store(_VQARADConf, name="VQARAD", group="datasets", provider="mmlearn") diff --git a/mmlearn/datasets/mimiciv_cxr.py b/mmlearn/datasets/mimiciv_cxr.py new file mode 100644 index 0000000..5e8f3d3 --- /dev/null +++ b/mmlearn/datasets/mimiciv_cxr.py @@ -0,0 +1,355 @@ +"""MIMIC-IV-CXR Dataset.""" + +import json +import logging +import os +from typing import Callable, Literal, Optional, get_args + +import numpy as np +import pandas as pd +import torch +from hydra_zen import MISSING, store +from PIL import Image +from torch.utils.data import Dataset +from torchvision.transforms import CenterCrop, Compose, Resize, ToTensor +from tqdm import tqdm + +from mmlearn.constants import EXAMPLE_INDEX_KEY +from mmlearn.datasets.core import Modalities +from mmlearn.datasets.core.example import Example + + +logger = logging.getLogger(__name__) + + +@store( + group="datasets", + provider="mmlearn", + root_dir=os.getenv("MIMICIVCXR_ROOT_DIR", MISSING), + split="train", + labeler="double_image", +) +class MIMICIVCXR(Dataset): + """Module to load image-text pairs from MIMIC-IV-CXR dataset. + + Parameters + ---------- + root_dir : str + Path to the directory containing json files which describe data entries. + split : {"train", "validate", "test"} + Dataset split. + labeler : {"chexpert", "negbio", "double_image", "single_image"} + Model which was used to generate labels from raw-text reports. + transform : Optional[Callable] + Custom transform applied to images. + tokenizer : Callable[[torch.Tensor], Dict[str, torch.Tensor]] + A function that tokenizes the raw text reports. + include_report : bool, default=False + Whether or not to include the raw text reports in the data example. + + Notes + ----- + Some datapoints have not been processed by the labelers; hence, they have no + assigned label. Note that this is different from all labels being set to `NaN`. + These datapoints are removed from the dataset if `include_report` is False. + Otherwise, the datapoints are included with an empty list ([]) for `label`. + + # Pre-processing + This module requires access to json files which contain data entries for each + labeler-split pair (i.e., 6 json files can be generated with the available 2 + labelers and 3 splits). These json files are generated by extracting relevant + information from existing csv files in the original dataset, and the path to where + the dataset is stored locally. Please refer to `CreateJSONFiles` class for more + information. + """ + + def __init__( + self, + root_dir: str, + split: Literal["train", "validate", "test"], + labeler: Literal["chexpert", "negbio", "double_image", "single_image"], + transform: Optional[Callable[[Image.Image], torch.Tensor]] = None, + tokenizer: Optional[Callable[[str], torch.Tensor]] = None, + include_report: bool = False, + ) -> None: + """Initialize the dataset.""" + # input validation + all_splits = ["train", "validate", "test"] + if split not in all_splits: + raise ValueError( + f"Split {split} is not available. Valid splits are {all_splits}." + ) + + all_labelers = ["chexpert", "negbio", "double_image", "single_image"] + if labeler not in all_labelers: + raise ValueError( + f"Labeler {labeler} is not available. Valid splits are {all_labelers}." + ) + + if transform is not None and not callable(transform): + raise ValueError("`transform` is not callable.") + + self.image_root = root_dir + data_path = os.path.join( + root_dir, + "mimic_cxr_" + + labeler + + "_" + + split + + (".json" if labeler in ["chexpert", "negbio"] else ".csv"), + ) + if not os.path.exists(data_path): + raise RuntimeError(f"Entries file is not accessible: {data_path}.") + self._labeler = labeler + + if self._labeler in ["double_image", "single_image"]: + df = pd.read_csv(data_path) + df = df.dropna(subset=["caption"]) # some captions are missing + self.entries = df.to_dict("records") + else: + with open(data_path, "rb") as file: + entries = json.load(file) + + # remove entries with no label if reports are not requested either + old_num = len(entries) + entries_df = pd.DataFrame(entries) + entries_df = entries_df[entries_df["label"].apply(len) > 0] + self.entries = entries_df.to_dict("records") + logger.info( + f"{old_num - len(entries)} datapoints removed due to lack of a label." + ) + + if transform is not None: + self.transform = transform + else: + self.transform = Compose([Resize(224), CenterCrop(224), ToTensor()]) + self.tokenizer = tokenizer + self.include_report = include_report + + def __getitem__(self, idx: int) -> Example: + """Return all the images and the label vector of the idx'th study.""" + entry = self.entries[idx] + img_path = entry["image_path"] + + with Image.open( + img_path + if self._labeler in ["negbio", "chexpert"] + else os.path.join(self.image_root, img_path) + ) as img: + image = self.transform(img.convert("RGB")) + + example = Example({Modalities.RGB: image, EXAMPLE_INDEX_KEY: idx}) + + if self._labeler in ["negbio", "chexpert"]: + example["subject_id"] = entry["subject_id"] + example["study_id"] = entry["study_id"] + example["qid"] = entry["qid"] + if self.include_report: + with open(entry["report_path"], encoding="utf-8") as file: + report = file.read() + tokenized_report = ( + self.tokenizer(report) if self.tokenizer is not None else None + ) + example[Modalities.TEXT] = report + if tokenized_report is not None: + example[Modalities.TEXT] = tokenized_report + else: + example[Modalities.TEXT] = entry["caption"] + tokens = ( + self.tokenizer(entry["caption"]) if self.tokenizer is not None else None + ) + if tokens is not None: + if isinstance(tokens, dict): # output of HFTokenizer + assert ( + Modalities.TEXT in tokens + ), f"Missing key `{Modalities.TEXT}` in tokens." + example.update(tokens) + else: + example[Modalities.TEXT] = tokens + + return example + + def __len__(self) -> int: + """Return the length of the dataset.""" + return len(self.entries) + + +class CreateJSONFiles(object): + """Required pre-processing to use MIMICIVCXR module. + + `MIMICIVCXR` requires access to json files containing concise information about all + data entries assigned to a specific split and labeler. `CreateJSONFiles` creates + these json files from existing csv files in the dataset, and information about + where the dataset is stored on the system. Each json file contains a list of + entries; each entry is a dictionary with the following keys: + `image_path`: str + Absolute path to the image. + `report_path`: str + Absolute path to the textual report. + `label`: List[int | NaN] + List of labels assigned to the data sample by the desired labeler. + `qid`: int + Query ID. This ID enables tracking the sample in the original dataset. + `subject_id`: int + ID of the patient. + `study_id`: int + ID of this specific X-ray study of the patient. + `dicom_id`: int + ID of the original X-ray DICOM image before it was converted to JPG. + + Parameters + ---------- + data_root : str + Path to the root directory of JPG image dataset; + e.g., `mimic-cxr/physionet.org/files/mimic-cxr-jpg/2.0.0`. + report_root : str + Path to the root directory of DICOM image dataset where the textual reports are + also stored; e.g., `mimic-cxr/physionet.org/files/mimic-cxr/2.0.0`. + json_root : str + Directory where the resulting json files will be stored. + skip_all_nan : bool, default=False + Skip datapoints with all labels assigned NaN. + skip_no_label : bool, default=False + Skip datapoints with no assigned labels; i.e. no entry in the labeler's csv + file. + nan_val : float, default=float('nan') + Value to replace with NaN labels. + """ + + def __init__( + self, + data_root: str, + report_root: str, + json_root: str, + skip_all_nan: bool = False, + skip_no_label: bool = False, + nan_val: float = float("nan"), + ) -> None: + """Initialize the module.""" + for path in [data_root, report_root, json_root]: + if not os.path.exists(path): + raise RuntimeError(f"Directory is not accessible: {path}.") + + self.data_root = data_root + self.report_root = report_root + self.json_root = json_root + self.skip_all_nan = skip_all_nan + self.skip_no_label = skip_no_label + self.nan_val = nan_val + + def make( + self, + labeler: Literal["chexpert", "negbio"], + split: Literal["train", "validate", "test"], + ) -> None: + """Make json file for the given labeler and split. + + Parameters + ---------- + labeler : {"chexpert", "negbio"} + Model which was used to generate labels from raw-text reports. + split : {"train", "validate", "test"} + Dataset split. + """ + # input validation + all_splits = ["train", "validate", "test"] + if split not in all_splits: + raise ValueError( + f"Split {split} is not available. Valid splits are {all_splits}." + ) + + all_labelers = ["chexpert", "negbio"] + if labeler not in all_labelers: + raise ValueError( + f"Labeler {labeler} is not available. Valid splits are {all_labelers}." + ) + + # load the splits file + split_df = pd.read_csv( + os.path.join(self.data_root, "mimic-cxr-2.0.0-split.csv.gz"), + compression="gzip", + ) + split_df = split_df.loc[split_df["split"] == split] + + # read the labels file + label_path = os.path.join( + self.data_root, "mimic-cxr-2.0.0-" + labeler + ".csv.gz" + ) + label_df = pd.read_csv(label_path, compression="gzip") + + entries = [] + for index, row in tqdm(split_df.iterrows(), total=len(split_df.index)): + subject_id = "p" + str(int(row["subject_id"])) + study_id = "s" + str(int(row["study_id"])) + image_name = row["dicom_id"] + ".jpg" + image_path = os.path.join( + self.data_root, + "files", + subject_id[:3], + subject_id, + study_id, + image_name, + ) + report_path = os.path.join( + self.report_root, "files", subject_id[:3], subject_id, study_id + ".txt" + ) + + if not os.path.exists(image_path): + print(f"File does not exit {image_path}.") + continue + + label_row = label_df.query( + "subject_id==@row['subject_id'] and study_id==@row['study_id']" + ) + if len(label_row) < 1: + print( + f"No entry with subject_id#{subject_id}, study_id#{study_id} in file {label_path}." + ) + # drop rows that have no label entry; + # note: this is different from having all NaN labels + if self.skip_no_label: + continue + label = np.array([]) + else: + label = label_row.iloc[0][2:].to_numpy() + + # skip rows that have no assigned labels + if self.skip_all_nan and pd.isna(label).all() and len(label) > 0: + continue + + # convert all NaN values to a given value, default is NaN itself + label[pd.isna(label)] = self.nan_val + + entry = { + "dicom_id": row["dicom_id"], + "subject_id": row["subject_id"], + "study_id": row["study_id"], + "qid": index, + "image_path": image_path, + "report_path": report_path, + "label": label.tolist(), + } + entries.append(entry) + + # dump to file + output_file = f"{labeler}_{split}.json" + with open(os.path.join(self.json_root, output_file), "w") as file: + json.dump(entries, file) + + print(f"Data dumped to {output_file} ({len(entries)} entries).") + + def make_all(self) -> None: + """Make json files for all labelers and splits. + + MIMIC-IV-CXR contains two labelers ("negbio" and "chexpert") and three splits + ("train", "validate" and "test"); hence, six json files will be created by this + method. + """ + all_labelers = Literal["chexpert", "negbio"] + all_splits = Literal["validate", "test", "train"] + for labeler in get_args(all_labelers): + for split in get_args(all_splits): + print( + f"Creating json file for labeler '{labeler}' and split '{split}'." + ) + self.make(labeler, split) diff --git a/mmlearn/datasets/nihcxr.py b/mmlearn/datasets/nihcxr.py new file mode 100644 index 0000000..a1a7908 --- /dev/null +++ b/mmlearn/datasets/nihcxr.py @@ -0,0 +1,112 @@ +"""NIH Chest X-ray Dataset.""" + +import json +import os +from typing import Callable, List, Literal, Optional + +import torch +from hydra_zen import MISSING, store +from PIL import Image +from torch.utils.data import Dataset +from torchvision.transforms import CenterCrop, Compose, Resize, ToTensor + +from mmlearn.constants import EXAMPLE_INDEX_KEY +from mmlearn.datasets.core import Modalities +from mmlearn.datasets.core.example import Example + + +# NIH Chest X-ray disease labels +_LABELS = [ + "Atelectasis", + "Cardiomegaly", + "Effusion", + "Infiltration", + "Mass", + "Nodule", + "Pneumonia", + "Pneumothorax", + "Consolidation", + "Edema", + "Emphysema", + "Fibrosis", + "Pleural_Thickening", + "Hernia", + "No Finding", +] + + +def text_labels() -> List[str]: + """Return a list of the NIH Chest X-ray textual labels.""" + return _LABELS + + +@store( + group="datasets", + provider="mmlearn", + root_dir=os.getenv("NIH_CXR_DIR", MISSING), + split="train", +) +class NIHCXR(Dataset[Example]): + """Module to load image-text pairs from NIH Chest Xray dataset. + + Parameters + ---------- + root_dir : str + Directory which contains json files stating all dataset entries. + split : {"train", "test", "bbox"} + Dataset split. 'bbox' is a subset of 'test' which contains bounding box info. + transform : callable, optional, default=None + Transforms applied to images. + """ + + def __init__( + self, + root_dir: str, + split: Literal["train", "test", "bbox"], + transform: Optional[Callable[[Image.Image], torch.Tensor]] = None, + ) -> None: + """Initialize the dataset.""" + assert split in ["train", "test", "bbox"], f"split {split} is not available." + assert ( + callable(transform) or transform is None + ), "transform is not callable or None." + + data_path = os.path.join(root_dir, split + "_data.json") + + assert os.path.isfile(data_path), f"entries file does not exist: {data_path}." + + with open(data_path, "rb") as file: + entries = json.load(file) + self.entries = entries + + if transform is not None: + self.transform = transform + else: + self.transform = Compose([Resize(224), CenterCrop(224), ToTensor()]) + + self.bbox = split == "bbox" + + def __getitem__(self, idx: int) -> Example: + """Return image-label or image-label-tabular(bbox).""" + entry = self.entries[idx] + image = Image.open(entry["image_path"]).convert("RGB") + image = self.transform(image) + label = torch.tensor(entry["label"]) + + example = Example( + { + Modalities.RGB: image, + Modalities.RGB.target: label, + "qid": entry["qid"], + EXAMPLE_INDEX_KEY: idx, + } + ) + + if self.bbox: + example["bbox"] = entry["bbox"] + + return example + + def __len__(self) -> int: + """Return the length of the dataset.""" + return len(self.entries) diff --git a/mmlearn/datasets/nyuv2.py b/mmlearn/datasets/nyuv2.py new file mode 100644 index 0000000..1f5fce0 --- /dev/null +++ b/mmlearn/datasets/nyuv2.py @@ -0,0 +1,201 @@ +"""SUN RGB-D dataset.""" + +import os +from typing import Callable, List, Literal, Optional + +import cv2 +import numpy as np +import torch +from hydra_zen import MISSING, store +from PIL import Image +from PIL.Image import Image as PILImage +from torch.utils.data import Dataset +from torchvision.transforms.v2.functional import to_pil_image + +from mmlearn.constants import EXAMPLE_INDEX_KEY +from mmlearn.datasets.core import Modalities +from mmlearn.datasets.core.example import Example + + +_LABELS = [ + "bedroom", + "kitchen", + "living room", + "bathroom", + "dining room", + "office", + "furniture store", + "classroom", + "home office", + "bookstore", +] + + +def text_labels() -> List[str]: + """Return a list of the CheXpert dataset's textual labels.""" + return _LABELS + + +def depth_normalize( + depth_file: str, min_depth: float = 0.01, max_depth: int = 50 +) -> torch.Tensor: + """Load depth file and convert to disparity. + + Parameters + ---------- + depth_file : str + Path to the depth file. + Sensor type of the depth file. + min_depth : float, default=0.01 + Minimum depth value to clip the depth image. + max_depth : int, default=50 + Maximum depth value to clip the depth image. + + Returns + ------- + torch.Tensor + Normalized depth image. + """ + depth_image = np.array(Image.open(depth_file)) + depth = np.array(depth_image).astype(np.float32) + depth_in_meters = depth / 1000.0 + + if min_depth is not None: + depth_in_meters = depth_in_meters.clip(min=min_depth, max=max_depth) + + return torch.from_numpy(depth_in_meters).float() + + +@store( + name="NYUv2", + group="datasets", + provider="mmlearn", + root_dir=os.getenv("NYUV2_ROOT_DIR", MISSING), +) +class NYUv2Dataset(Dataset[Example]): + """NYUv2 dataset. + + Parameters + ---------- + root_dir : str + Path to the root directory of the dataset. + split : {"train", "test"}, default="train" + Split of the dataset to use. + return_type : {"disparity", "image"}, default="disparity" + Return type of the depth images. + Disparity: Return the depth image as disparity map. + Image: Return the depth image as a 3-channel image. + rgb_transform: Optional[Callable[[PILImage], torch.Tensor]], default=None + Transform to apply to the RGB images. + depth_transform: Optional[Callable[[PILImage], torch.Tensor]], default=None + Transform to apply to the depth images. + """ + + def __init__( + self, + root_dir: str, + split: Literal["train", "test"] = "train", + return_type: Literal["disparity", "image"] = "disparity", + rgb_transform: Optional[Callable[[PILImage], torch.Tensor]] = None, + depth_transform: Optional[Callable[[PILImage], torch.Tensor]] = None, + ) -> None: + super().__init__() + self._validate_args(root_dir, split, rgb_transform, depth_transform) + self.return_type = return_type + + self.root_dir = root_dir + with open(os.path.join(root_dir, f"{split}.txt"), "r") as f: + file_ids = f.readlines() + file_ids = [f.strip() for f in file_ids] + + root_dir = os.path.join(root_dir, split) + depth_files = [os.path.join(root_dir, "depth", f"{f}.png") for f in file_ids] + rgb_files = [os.path.join(root_dir, "rgb", f"{f}.jpg") for f in file_ids] + + label_files = [ + os.path.join(root_dir, "scene_class", f"{f}.txt") for f in file_ids + ] + labels = [str(open(f).read().strip()) for f in label_files] # noqa: SIM115 + labels = [label.replace("_", " ") for label in labels] + labels = [ + _LABELS.index(label) if label in _LABELS else len(_LABELS) # type: ignore + for label in labels + ] + + # remove the samples with classes not in _LABELS + # this is to follow the same classes used in ImageBind + if split == "test": + valid_indices = [ + i + for i, label in enumerate(labels) + if label < len(_LABELS) # type: ignore + ] + rgb_files = [rgb_files[i] for i in valid_indices] + depth_files = [depth_files[i] for i in valid_indices] + labels = [labels[i] for i in valid_indices] + + self.samples = list(zip(rgb_files, depth_files, labels)) + + self.rgb_transform = rgb_transform + self.depth_transform = depth_transform + + def __len__(self) -> int: + """Return the length of the dataset.""" + return len(self.samples) + + def _validate_args( + self, + root_dir: str, + split: str, + rgb_transform: Optional[Callable[[PILImage], torch.Tensor]], + depth_transform: Optional[Callable[[PILImage], torch.Tensor]], + ) -> None: + """Validate arguments.""" + if not os.path.isdir(root_dir): + raise NotADirectoryError( + f"The given `root_dir` {root_dir} is not a directory", + ) + if split not in ["train", "test"]: + raise ValueError( + f"Expected `split` to be one of `'train'` or `'test'`, but got {split}", + ) + if rgb_transform is not None and not callable(rgb_transform): + raise TypeError( + f"Expected argument `rgb_transform` to be callable, but got {type(rgb_transform)}", + ) + if depth_transform is not None and not callable(depth_transform): + raise TypeError( + f"Expected `depth_transform` to be callable, but got {type(depth_transform)}", + ) + + def __getitem__(self, idx: int) -> Example: + """Return RGB and depth images at index `idx`.""" + # Read images + rgb_image = cv2.imread(self.samples[idx][0], cv2.IMREAD_UNCHANGED) + if self.rgb_transform is not None: + rgb_image = self.rgb_transform(to_pil_image(rgb_image)) + + if self.return_type == "disparity": + depth_image = depth_normalize( + self.samples[idx][1], + ) + else: + # Using cv2 instead of PIL Image since we use PNG grayscale images. + depth_image = cv2.imread( + self.samples[idx][1], + cv2.IMREAD_GRAYSCALE, + ) + # Make a 3-channel depth image to enable passing to a pretrained ViT. + depth_image = np.repeat(depth_image[:, :, np.newaxis], 3, axis=-1) + + if self.depth_transform is not None: + depth_image = self.depth_transform(to_pil_image(depth_image)) + + return Example( + { + Modalities.RGB: rgb_image, + Modalities.DEPTH: depth_image, + EXAMPLE_INDEX_KEY: idx, + Modalities.DEPTH.target: self.samples[idx][2], + } + ) diff --git a/mmlearn/datasets/pmcoa.py b/mmlearn/datasets/pmcoa.py new file mode 100644 index 0000000..9d13149 --- /dev/null +++ b/mmlearn/datasets/pmcoa.py @@ -0,0 +1,162 @@ +"""PMC-OA dataset.""" + +import os +from typing import Any, Callable, Dict, Literal, Optional, Tuple + +import jsonlines +import pandas as pd +import torch +from hydra_zen import MISSING, store +from PIL import Image +from torch.utils.data import Dataset +from torchvision import transforms + +from mmlearn.constants import EXAMPLE_INDEX_KEY +from mmlearn.datasets.core import Modalities +from mmlearn.datasets.core.example import Example + + +@store( + group="datasets", + provider="mmlearn", + root_dir=os.getenv("PMCOA_ROOT_DIR", MISSING), +) +class PMCOA(Dataset[Example]): + """Handles loading and processing of the PMC-OA dataset.""" + + def __init__( + self, + root_dir: str, + split: Literal["train", "valid", "test"] = "train", + file_type: str = "jsonl", + image_key: str = "image", + caption_key: str = "caption", + csv_separator: str = ",", + transform: Optional[Callable[[Image.Image], torch.Tensor]] = None, + tokenizer: Optional[Callable[[str], torch.Tensor]] = None, + mask_generator: Optional[ + Callable[ + [Dict[str, torch.Tensor], Any], + Tuple[torch.Tensor, torch.Tensor, torch.Tensor], + ] + ] = None, + image_dir: Optional[str] = None, + ) -> None: + """Initialize the dataset object with file paths and configurations. + + Parameters + ---------- + root_dir : str + Directory where the dataset is stored. + split : str, default="train" + Split of the dataset (train, valid, test). + file_type : str, default="jsonl" + Type of the input file (csv or jsonl). + img_key : str, default="image" + Key for images in the CSV/JSONL files. + caption_key : str, default="caption" + Key for captions in the CSV/JSONL files. + csv_separator : str, default="," + Separator used in CSV files. Not used for JSONL. + transform : Callable, optional, default=None + Transform applied to images. + tokenizer : Callable[[torch.Tensor], Dict[str, torch.Tensor]] + Text tokenizer. + mask_generator : Callable[[Dict[str, torch.Tensor], Any], Tuple[torch.Tensor, torch.Tensor, torch.Tensor]]], optional, default=None + Generator for the mask. + image_dir : str, optional, default=None + Directory where images are stored, relative to the root directory. + If not provided, it is assumed to be `'images'`. + """ # noqa: W505 + if split not in ["train", "valid", "test"]: + raise ValueError( + "Invalid split name. Split must be one of 'train', 'valid', or 'test'." + ) + if file_type not in ["csv", "jsonl"]: + raise ValueError( + "Invalid file type. File type must be one of 'csv' or 'jsonl'." + ) + + self.root_dir = root_dir + + if image_dir is None: + self.image_dir = "images" + else: + self.image_dir = image_dir + + self.split = split + input_filename = os.path.join(root_dir, f"{self.split}.{file_type}") + + self.image_filenames, self.captions = ( + self._csv_loader(input_filename, image_key, caption_key, csv_separator) + if file_type == "csv" + else self._jsonl_loader(input_filename, image_key, caption_key) + ) + + if transform is None: + self.transform = transforms.ToTensor() + else: + self.transform = transform + self.tokenizer = tokenizer + self.mask_generator = mask_generator + + def __len__(self) -> int: + """Return the length of the dataset.""" + return len(self.captions) + + def __getitem__(self, idx: int) -> Example: + """Return items in the dataset.""" + image_path = os.path.join( + self.root_dir, self.image_dir, self.image_filenames[idx] + ) + + with Image.open(image_path) as img: + images = self.transform(img) + + caption = str(self.captions[idx]) + example = Example( + { + Modalities.RGB: images, + Modalities.TEXT: caption, + EXAMPLE_INDEX_KEY: idx, + } + ) + + tokens = self.tokenizer(caption) if self.tokenizer is not None else None + if tokens is not None: + if isinstance(tokens, dict): # output of HFTokenizer + assert ( + Modalities.TEXT in tokens + ), f"Missing key `{Modalities.TEXT}` in tokens." + example.update(tokens) + else: + example[Modalities.TEXT] = tokens + + if self.mask_generator is not None and self.tokenizer is not None: + _, masked_labels, masked_text = self.mask_generator( + tokens, + self.tokenizer.tokenizer, # type: ignore + ) + example[Modalities.TEXT.mask] = masked_text + example[Modalities.TEXT.target] = masked_labels + + return example + + def _csv_loader( + self, input_filename: str, img_key: str, caption_key: str, sep: str + ) -> Tuple[Any, Any]: + """Load images, captions from CSV data.""" + df = pd.read_csv(input_filename, sep=sep) + images, captions = df[img_key].tolist(), df[caption_key].tolist() + return images, captions + + def _jsonl_loader( + self, input_filename: str, img_key: str, caption_key: str + ) -> Tuple[Any, Any]: + """Load images, captions from JSON data.""" + images, captions = [], [] + with jsonlines.open(input_filename) as reader: + for obj in reader: + images.append(obj[img_key]) + captions.append(obj[caption_key]) + return images, captions diff --git a/mmlearn/datasets/processors/__init__.py b/mmlearn/datasets/processors/__init__.py new file mode 100644 index 0000000..85665aa --- /dev/null +++ b/mmlearn/datasets/processors/__init__.py @@ -0,0 +1,22 @@ +"""Data processors.""" + +from mmlearn.datasets.processors.masking import ( + BlockwiseImagePatchMaskGenerator, + RandomMaskGenerator, +) +from mmlearn.datasets.processors.tokenizers import HFTokenizer +from mmlearn.datasets.processors.transforms import ( + MedVQAProcessor, + TrimText, + med_clip_vision_transform, +) + + +__all__ = [ + "BlockwiseImagePatchMaskGenerator", + "HFTokenizer", + "MedVQAProcessor", + "RandomMaskGenerator", + "TrimText", + "med_clip_vision_transform", +] diff --git a/mmlearn/datasets/processors/masking.py b/mmlearn/datasets/processors/masking.py new file mode 100644 index 0000000..0748020 --- /dev/null +++ b/mmlearn/datasets/processors/masking.py @@ -0,0 +1,227 @@ +"""Token mask generators.""" + +import math +import random +from typing import Any, Optional, Tuple, Union + +import torch +from hydra_zen import store +from transformers.tokenization_utils_base import PreTrainedTokenizerBase + + +@store(group="datasets/masking", provider="mmlearn", probability=0.15) +class RandomMaskGenerator: + """Random mask generator. + + Returns a random mask of shape `(nb_patches, nb_patches)` based on the + configuration where the number of patches to be masked is num_masking_patches. + + Parameters + ---------- + probability : float + Probability of masking a token. + """ + + def __init__(self, probability: float): + self.probability = probability + + def __call__( + self, + inputs: torch.Tensor, + tokenizer: PreTrainedTokenizerBase, + special_tokens_mask: Optional[torch.Tensor] = None, + ) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]: + """Generate a random mask. + + Returns a random mask of shape (nb_patches, nb_patches) based on the + configuration where the number of patches to be masked is num_masking_patches. + + Returns + ------- + inputs : torch.Tensor + The encoded inputs. + tokenizer : PreTrainedTokenizer + The tokenizer. + special_tokens_mask : Optional[torch.Tensor], default=None + Mask for special tokens. + """ + inputs = tokenizer.pad(inputs, return_tensors="pt")["input_ids"] + labels = inputs.clone() + # We sample a few tokens in each sequence for MLM training + # (with probability `self.probability`) + probability_matrix = torch.full(labels.shape, self.probability) + if special_tokens_mask is None: + special_tokens_mask = tokenizer.get_special_tokens_mask( + labels, already_has_special_tokens=True + ) + special_tokens_mask = torch.tensor(special_tokens_mask, dtype=torch.bool) + else: + special_tokens_mask = special_tokens_mask.bool() + + probability_matrix.masked_fill_(special_tokens_mask, value=0.0) + masked_indices = torch.bernoulli(probability_matrix).bool() + labels[~masked_indices] = tokenizer.pad_token_id + # 80% of the time, replace masked input tokens with tokenizer.mask_token([MASK]) + indices_replaced = ( + torch.bernoulli(torch.full(labels.shape, 0.8)).bool() & masked_indices + ) + inputs[indices_replaced] = tokenizer.convert_tokens_to_ids(tokenizer.mask_token) + + # 10% of the time, we replace masked input tokens with random word + indices_random = ( + torch.bernoulli(torch.full(labels.shape, 0.5)).bool() + & masked_indices + & ~indices_replaced + ) + random_words = torch.randint(len(tokenizer), labels.shape, dtype=torch.long) + inputs[indices_random] = random_words[indices_random] + + # Rest of the time (10% of the time) we keep the masked input tokens unchanged + return inputs, labels, masked_indices + + +@store(group="datasets/masking", provider="mmlearn") +class BlockwiseImagePatchMaskGenerator: + """Blockwise image patch mask generator. + + Parameters + ---------- + input_size: int or Tuple[int, int] + The size of the input image. + num_masking_patches: int + The number of patches to be masked. + min_num_patches: int, default=4 + The minimum number of patches to be masked. + max_num_patches: int, default=None + The maximum number of patches to be masked. + min_aspect_ratio: float, default=0.3 + The minimum aspect ratio of the patch. + max_aspect_ratio: float, default=None + The maximum aspect ratio of the patch. + """ + + def __init__( + self, + input_size: Union[int, Tuple[int, int]], + num_masking_patches: int, + min_num_patches: int = 4, + max_num_patches: Any = None, + min_aspect_ratio: float = 0.3, + max_aspect_ratio: Any = None, + ): + if not isinstance(input_size, tuple): + input_size = (input_size,) * 2 + self.height, self.width = input_size + + self.num_masking_patches = num_masking_patches + + self.min_num_patches = min_num_patches + self.max_num_patches = ( + num_masking_patches if max_num_patches is None else max_num_patches + ) + + max_aspect_ratio = max_aspect_ratio or 1 / min_aspect_ratio + self.log_aspect_ratio = (math.log(min_aspect_ratio), math.log(max_aspect_ratio)) + + def __repr__(self) -> str: + """Generate a printable representation. + + Returns + ------- + str + A printable representation of the object. + + """ + return "Generator(%d, %d -> [%d ~ %d], max = %d, %.3f ~ %.3f)" % ( + self.height, + self.width, + self.min_num_patches, + self.max_num_patches, + self.num_masking_patches, + self.log_aspect_ratio[0], + self.log_aspect_ratio[1], + ) + + def get_shape(self) -> Tuple[int, int]: + """Get the shape of the mask.""" + return self.height, self.width + + def _mask(self, mask: torch.Tensor, max_mask_patches: int) -> int: + """Masking function. + + This function mask adjacent patches by first selecting a target area and aspect + ratio. Since, there might be overlap between selected areas or the selected + area might already be masked, it runs for a maximum of 10 attempts or until the + specified number of patches (max_mask_patches) is achieved. + + + Parameters + ---------- + mask: torch.Tensor + Current mask. The mask to be updated. + max_mask_patches: int + The maximum number of patches to be masked. + + Returns + ------- + delta: int + The number of patches that were successfully masked. + + Notes + ----- + - `target_area`: Randomly chosen target area for the patch. + - `aspect_ratio`: Randomly chosen aspect ratio for the patch. + - `h`: Height of the patch based on the target area and aspect ratio. + - `w`: Width of the patch based on the target area and aspect ratio. + - `top`: Randomly chosen top position for the patch. + - `left`: Randomly chosen left position for the patch. + - `num_masked`: Number of masked pixels within the proposed patch area. + - `delta`: Accumulated count of modified pixels. + """ + delta = 0 + for _ in range(10): + target_area = random.uniform(self.min_num_patches, max_mask_patches) + aspect_ratio = math.exp(random.uniform(*self.log_aspect_ratio)) + h = int(round(math.sqrt(target_area * aspect_ratio))) + w = int(round(math.sqrt(target_area / aspect_ratio))) + if w < self.width and h < self.height: + top = random.randint(0, self.height - h) + left = random.randint(0, self.width - w) + + num_masked = mask[top : top + h, left : left + w].sum() + # Overlap + if 0 < h * w - num_masked <= max_mask_patches: + for i in range(top, top + h): + for j in range(left, left + w): + if mask[i, j] == 0: + mask[i, j] = 1 + delta += 1 + + if delta > 0: + break + return delta + + def __call__(self) -> torch.Tensor: + """Generate a random mask. + + Returns a random mask of shape (nb_patches, nb_patches) based on the + configuration where the number of patches to be masked is num_masking_patches. + + Returns + ------- + mask: torch.Tensor + A mask of shape (nb_patches, nb_patches) + + """ + mask = torch.zeros(self.get_shape(), dtype=torch.int) + mask_count = 0 + while mask_count < self.num_masking_patches: + max_mask_patches = self.num_masking_patches - mask_count + max_mask_patches = min(max_mask_patches, self.max_num_patches) + + delta = self._mask(mask, max_mask_patches) + if delta == 0: + break + mask_count += delta + + return mask diff --git a/mmlearn/datasets/processors/tokenizers.py b/mmlearn/datasets/processors/tokenizers.py new file mode 100644 index 0000000..5fef6b7 --- /dev/null +++ b/mmlearn/datasets/processors/tokenizers.py @@ -0,0 +1,188 @@ +"""Tokenizers - modules that convert raw input to sequences of tokens.""" + +from typing import Any, Dict, List, Optional, Tuple, Union + +import torch +from hydra_zen import store +from torch import nn +from transformers import AutoTokenizer + +from mmlearn.datasets.core import Modalities + + +@store(group="datasets/tokenizers", provider="mmlearn") +class HFTokenizer: + """HuggingFace tokenizer wrapper. + + This class wraps any huggingface tokenizer that can be initialized with + `AutoTokenizer.from_pretrained`. It preprocesses the input text and returns + the tokenized output. + + Parameters + ---------- + model_name_or_path : str + Pretrained model name or path - same as in `AutoTokenizer.from_pretrained`. + max_length : int, optional, default=None + Maximum length of the tokenized sequence. This is passed to the tokenizer + `__call__` method. + padding : bool or str, default=False + Padding strategy. Same as in `AutoTokenizer.from_pretrained`; passed to + the tokenizer `__call__` method. + truncation : bool or str, optional, default=None + Truncation strategy. Same as in `AutoTokenizer.from_pretrained`; passed to + the tokenizer `__call__` method. + **kwargs : Any + Additional arguments passed to `AutoTokenizer.from_pretrained`. + """ + + def __init__( + self, + model_name_or_path: str, + max_length: Optional[int] = None, + padding: Union[bool, str] = False, + truncation: Optional[Union[bool, str]] = None, + **kwargs: Any, + ) -> None: + self.tokenizer = AutoTokenizer.from_pretrained(model_name_or_path, **kwargs) + self.max_length = max_length + self.padding = padding + self.truncation = truncation + + def __call__( + self, sentence: Union[str, List[str]], **kwargs: Any + ) -> Dict[str, torch.Tensor]: + """Tokenize a text or a list of texts using the HuggingFace tokenizer. + + Parameters + ---------- + sentence : str or list of str + Sentence(s) to be tokenized. + **kwargs : Any + Additional arguments passed to the tokenizer `__call__` method. + + Returns + ------- + Dict[str, torch.Tensor] + Tokenized sentence(s). + + Notes + ----- + The 'input_ids' key is replaced with 'Modalities.TEXT' for consistency. + """ + batch_encoding = self.tokenizer( + sentence, + max_length=self.max_length, + padding=self.padding, + truncation=self.truncation, + return_tensors="pt", + **kwargs, + ) + + if isinstance( + sentence, str + ): # remove batch dimension if input is a single sentence + for key, value in batch_encoding.items(): + if isinstance(value, torch.Tensor): + batch_encoding[key] = torch.squeeze(value, 0) + + # replace 'input_ids' with 'Modalities.TEXT' for consistency + batch_encoding[Modalities.TEXT] = batch_encoding["input_ids"] + return dict(batch_encoding) + + +store( + HFTokenizer, + name="HFCLIPTokenizer", + group="datasets/tokenizers", + model_name_or_path="openai/clip-vit-base-patch16", + max_length=77, + padding="max_length", + truncation=True, +) + + +def patchify(batch: torch.Tensor, patch_size: Tuple[int, int]) -> torch.Tensor: + """Patchify a batch of images. + + Parameters + ---------- + batch : torch.Tensor + Batch of images. + patch_size : tuple of int + The size of the patch. + + Returns + ------- + torch.Tensor + Patchified batch. + + Notes + ----- + - Input shape: (b, h, w, c) + - Output shape: (b, nh, nw, ph, pw, c) + + """ + b, c, h, w = batch.shape + ph, pw = patch_size + nh, nw = h // ph, w // pw + + batch_patches = torch.reshape(batch, (b, c, nh, ph, nw, pw)) + return torch.permute(batch_patches, (0, 1, 2, 4, 3, 5)) + + +class Img2Seq(nn.Module): + """Convert a batch of images to a batch of sequences. + + Parameters + ---------- + img_size : tuple of int + The size of the input image. + patch_size : tuple of int + The size of the patch. + n_channels : int + The number of channels in the input image. + d_model : int + The dimension of the output sequence. + + Notes + ----- + - Input shape: (b, h, w, c) + - Output shape: (b, s, d) + + """ + + def __init__( + self, + img_size: Tuple[int, int], + patch_size: Tuple[int, int], + n_channels: int, + d_model: int, + ) -> None: + """Initialize the Img2Seq module.""" + super().__init__() + self.patch_size = patch_size + self.img_size = img_size + + nh, nw = img_size[0] // patch_size[0], img_size[1] // patch_size[1] + n_tokens = nh * nw + + token_dim = patch_size[0] * patch_size[1] * n_channels + self.linear = nn.Linear(token_dim, d_model) + self.cls_token = nn.Parameter(torch.randn(1, 1, d_model)) + self.pos_emb = nn.Parameter(torch.randn(n_tokens, d_model)) + + def __call__(self, batch: torch.Tensor) -> torch.Tensor: + """Convert a batch of images to a batch of sequences.""" + batch = patchify(batch, self.patch_size) + + b, c, nh, nw, ph, pw = batch.shape + + # Flattening the patches + batch = torch.permute(batch, [0, 2, 3, 4, 5, 1]) + batch = torch.reshape(batch, [b, nh * nw, ph * pw * c]) + + batch = self.linear(batch) + cls: torch.Tensor = self.cls_token.expand([b, -1, -1]) + emb: torch.Tensor = batch + self.pos_emb + + return torch.cat([cls, emb], axis=1) diff --git a/mmlearn/datasets/processors/transforms.py b/mmlearn/datasets/processors/transforms.py new file mode 100644 index 0000000..04ed5dd --- /dev/null +++ b/mmlearn/datasets/processors/transforms.py @@ -0,0 +1,101 @@ +"""Custom transforms for datasets.""" + +from typing import List, Literal, Union + +from hydra_zen import store +from timm.data.transforms import ResizeKeepRatio +from torchvision import transforms + + +@store(group="datasets/transforms", provider="mmlearn") +class TrimText: + """Trim text strings as a preprocessing step before tokenization.""" + + def __init__(self, trim_size: int) -> None: + """Initialize the object.""" + self.trim_size = trim_size + + def __call__(self, sentence: Union[str, List[str]]) -> Union[str, List[str]]: + """Trim the given sentence(s).""" + if not isinstance(sentence, (list, str)): + raise TypeError( + "Expected argument `sentence` to be a string or list of strings, " + f"but got {type(sentence)}" + ) + + if isinstance(sentence, str): + return sentence[: self.trim_size] + + for i, s in enumerate(sentence): + sentence[i] = s[: self.trim_size] + + return sentence + + +class MedVQAProcessor: + """Preprocessor for textual reports of MedVQA datasets.""" + + def __call__(self, sentence: Union[str, List[str]]) -> Union[str, List[str]]: + """Process the textual captions.""" + if not isinstance(sentence, (list, str)): + raise TypeError( + f"Expected sentence to be a string or list of strings, got {type(sentence)}" + ) + + def _preprocess_sentence(sentence: str) -> str: + sentence = sentence.lower() + if "? -yes/no" in sentence: + sentence = sentence.replace("? -yes/no", "") + if "? -open" in sentence: + sentence = sentence.replace("? -open", "") + if "? - open" in sentence: + sentence = sentence.replace("? - open", "") + return ( + sentence.replace(",", "") + .replace("?", "") + .replace("'s", " 's") + .replace("...", "") + .replace("x ray", "x-ray") + .replace(".", "") + ) + + if isinstance(sentence, str): + return _preprocess_sentence(sentence) + + for i, s in enumerate(sentence): + sentence[i] = _preprocess_sentence(s) + + return sentence + + +@store(group="datasets/transforms", provider="mmlearn") # type: ignore[misc] +def med_clip_vision_transform( + image_crop_size: int = 224, job_type: Literal["train", "eval"] = "train" +) -> transforms.Compose: + """Return transforms for training/evaluating CLIP with medical images. + + Parameters + ---------- + image_crop_size : int, default=224 + Size of the image crop. + job_type : {"train", "eval"}, default="train" + Type of the job (training or evaluation) for which the transforms are needed. + + Returns + ------- + transforms.Compose + Composed transforms for training CLIP with medical images. + """ + return transforms.Compose( + [ + ResizeKeepRatio(512, interpolation="bicubic"), + transforms.RandomCrop(image_crop_size) + if job_type == "train" + else transforms.CenterCrop(image_crop_size), + transforms.ToTensor(), + transforms.Normalize( + mean=[0.48145466, 0.4578275, 0.40821073], + std=[0.26862954, 0.26130258, 0.27577711], + ), + ] + ) diff --git a/mmlearn/datasets/quilt.py b/mmlearn/datasets/quilt.py new file mode 100644 index 0000000..8622f9e --- /dev/null +++ b/mmlearn/datasets/quilt.py @@ -0,0 +1,185 @@ +"""Quilt-1M Dataset.""" + +import ast +import os +from typing import Callable, List, Literal, Optional + +import pandas as pd +import torch +from hydra_zen import MISSING, store +from PIL import Image +from torch.utils.data import Dataset +from torchvision.transforms import CenterCrop, Compose, Resize, ToTensor + +from mmlearn.constants import EXAMPLE_INDEX_KEY +from mmlearn.datasets.core import Modalities +from mmlearn.datasets.core.example import Example + + +@store( + group="datasets", + provider="mmlearn", + root_dir=os.getenv("QUILT_ROOT_DIR", MISSING), +) +class Quilt(Dataset[Example]): + """Quilt-1M dataset. + + Parameters + ---------- + root_dir : str + Path to the root directory of the dataset. + split : {"train", "val"} + Dataset split. + subset : List[str], optional, default=["openpath", "pubmed", "quilt", "laion"] + Subsets of Quilt-1M to load. + transform : Optional[Callable] + Transform applied to images. + tokenizer : Optional[Callable], default=None + Function applied to textual captions. + processor : Optional[Callable], default=None + Function applied to the image-target pair. + + Notes + ----- + If `processor` is not None, it overrides `transform` and `tokenizer`. + """ + + def __init__( + self, + root_dir: str, + split: Literal["train", "val"] = "train", + subset: Optional[List[str]] = None, + transform: Optional[Callable[[Image.Image], torch.Tensor]] = None, + tokenizer: Optional[Callable[[str], torch.Tensor]] = None, + processor: Optional[ + Callable[[Image.Image, str], tuple[torch.Tensor, torch.Tensor]] + ] = None, + ) -> None: + """Initialize the dataset.""" + # input validation + if not os.path.exists(root_dir): + raise RuntimeError(f"Root directory is not accessible: {root_dir}.") + + all_splits = ["train", "val"] + if split not in all_splits: + raise ValueError( + f"Split {split} is not available. Valid splits are {all_splits}." + ) + + all_subsets = ["openpath", "pubmed", "quilt", "laion"] + if subset is None: + subset = all_subsets + for subset_name in subset: + if subset_name not in all_subsets: + raise ValueError( + f"Subset {subset_name} is not available. Valid subsets are {all_subsets}." + ) + + for func_name, func in zip( + ["transform", "tokenizer", "processor"], [transform, tokenizer, processor] + ): + if func is not None and not callable(func): + raise ValueError(f"`{func_name}` is not callable.") + + # read entries + self.data_df = pd.read_csv(os.path.join(root_dir, "quilt_1M_entries.csv")) + # drop unnecessary and space-consuming columns + self.data_df.drop( + columns=[ + "noisy_text", + "corrected_text", + "med_umls_ids", + "roi_text", + "Unnamed: 0", + ], + inplace=True, + ) + # filter entries based on `split` and `subset` + self.data_df = self.data_df.loc[ + self.data_df.apply( + lambda row: row["split"] == split and row["subset"] in subset, axis=1 + ) + ] + + # the 'pathology' column is a list of strings + self.data_df["pathology"] = self.data_df["pathology"].apply(_safe_eval) + + self.root_dir = root_dir + self.subset = subset + + if processor is None and transform is None: + self.transform = Compose([Resize(224), CenterCrop(224), ToTensor()]) + elif processor is None: + self.transform = transform + else: + self.transform = None + + if processor is None: + self.tokenizer = tokenizer + else: + self.tokenizer = None + + self.processor = processor + + def __getitem__(self, idx: int) -> Example: + """Return the idx'th data sample. + + If a tokenizer is not defined by `processor` or `tokenizer`, only the + image and free text caption are returned. Otherwise, the image, free- + text caption, and caption tokens are returned. + """ + try: + with Image.open( + os.path.join( + self.root_dir, "quilt_1m", self.data_df["image_path"].iloc[idx] + ) + ) as img: + image = img.convert("RGB") + except Exception as e: + print(f"ERROR: {e} on {self.data_df['image_path'].iloc[idx]}") + + if self.transform is not None: + image = self.transform(image) + + caption = self.data_df["caption"].iloc[idx] + tokens = self.tokenizer(caption) if self.tokenizer is not None else None + + if self.processor is not None: + image, tokens = self.processor(image, caption) + + example = Example( + { + Modalities.RGB: image, + Modalities.TEXT: caption, + EXAMPLE_INDEX_KEY: idx, + "qid": self.data_df.index[idx], + "magnification": self.data_df["magnification"].iloc[idx], + "height": self.data_df["height"].iloc[idx], + "width": self.data_df["width"].iloc[idx], + } + ) + + if tokens is not None: + if isinstance(tokens, dict): # output of HFTokenizer + assert ( + Modalities.TEXT in tokens + ), f"Missing key `{Modalities.TEXT}` in tokens." + example.update(tokens) + else: + example[Modalities.TEXT] = tokens + + return example + + def __len__(self) -> int: + """Return the length of the dataset.""" + return len(self.data_df.index) + + +def _safe_eval(x: str) -> list[str]: + """Safely evaluate a string as a list.""" + if pd.isna(x): + return [] + try: + return ast.literal_eval(x) # type: ignore[no-any-return] + except (ValueError, SyntaxError): + return [] diff --git a/mmlearn/datasets/roco.py b/mmlearn/datasets/roco.py new file mode 100644 index 0000000..bd907ef --- /dev/null +++ b/mmlearn/datasets/roco.py @@ -0,0 +1,120 @@ +"""ROCO Dataset.""" + +import json +import os +from typing import Callable, Dict, Literal, Optional, Union + +import torch +from hydra_zen import MISSING, store +from PIL import Image +from torch.utils.data import Dataset +from torchvision.transforms import CenterCrop, Compose, Resize, ToTensor + +from mmlearn.constants import EXAMPLE_INDEX_KEY +from mmlearn.datasets.core import Modalities +from mmlearn.datasets.core.example import Example + + +@store( + group="datasets", + provider="mmlearn", + root_dir=os.getenv("ROCO_ROOT_DIR", MISSING), +) +class ROCO(Dataset[Example]): + """ROCO dataset. + + Parameters + ---------- + root_dir : str + Path to the json file containing all entries of the dataset. + split : {"train", "validation", "test"} + Dataset split. + group : {"radiology", "non-radiology"}, default="radiology" + Dataset group. + transform : Optional[Callable], default=None + Transform applied to images. + tokenizer : Optional[Callable], default=None + Function applied to textual captions. + processor : Optional[Callable], default=None + Function applied to the image-target pair. + + Notes + ----- + If `processor` is not None, it overrides `transform` and `tokenizer`. + """ + + def __init__( + self, + root_dir: str, + split: Literal["train", "validation", "test"] = "train", + group: Literal["radiology", "non-radiology"] = "radiology", + transform: Optional[Callable[[Image.Image], torch.Tensor]] = None, + tokenizer: Optional[ + Callable[[str], Union[torch.Tensor, Dict[str, torch.Tensor]]] + ] = None, + processor: Optional[ + Callable[[Image.Image, str], tuple[torch.Tensor, torch.Tensor]] + ] = None, + ) -> None: + """Initialize the dataset.""" + data_path = os.path.join(root_dir, group + split + "_dataset.json") + with open(data_path, encoding="utf-8") as file: + entries = [json.loads(line) for line in file.readlines()] + self.entries = entries + + if processor is None and transform is None: + self.transform = Compose([Resize(224), CenterCrop(224), ToTensor()]) + elif processor is None: + self.transform = transform + else: + self.transform = None + + if processor is None: + self.tokenizer = tokenizer + else: + self.tokenizer = None + + self.processor = processor + + def __getitem__(self, idx: int) -> Example: + """Return the idx'th data sample. + + If a tokenizer is not defined by `processor` or `tokenizer`, only the + image and free text caption are returned. Otherwise, the image, free- + text caption, and caption tokens are returned. + """ + entry = self.entries[idx] + with Image.open(entry["image_path"]) as img: + image = img.convert("RGB") + + if self.transform is not None: + image = self.transform(image) + + caption = entry["caption"] + tokens = self.tokenizer(caption) if self.tokenizer is not None else None + + if self.processor is not None: + image, tokens = self.processor(image, caption) + + example = Example( + { + Modalities.RGB: image, + Modalities.TEXT: caption, + EXAMPLE_INDEX_KEY: idx, + } + ) + + if tokens is not None: + if isinstance(tokens, dict): # output of HFTokenizer + assert ( + Modalities.TEXT in tokens + ), f"Missing key `{Modalities.TEXT}` in tokens." + example.update(tokens) + else: + example[Modalities.TEXT] = tokens + + return example + + def __len__(self) -> int: + """Return the length of the dataset.""" + return len(self.entries) diff --git a/mmlearn/datasets/sunrgbd.py b/mmlearn/datasets/sunrgbd.py new file mode 100644 index 0000000..6f77126 --- /dev/null +++ b/mmlearn/datasets/sunrgbd.py @@ -0,0 +1,261 @@ +"""SUN RGB-D dataset.""" + +import os +from typing import Callable, List, Literal, Optional + +import cv2 +import numpy as np +import torch +from hydra_zen import MISSING, store +from PIL import Image +from PIL.Image import Image as PILImage +from torch.utils.data import Dataset +from torchvision.transforms.v2.functional import to_pil_image + +from mmlearn.constants import EXAMPLE_INDEX_KEY +from mmlearn.datasets.core import Modalities +from mmlearn.datasets.core.example import Example + + +_LABELS = [ + "bathroom", + "bedroom", + "classroom", + "computer room", + "conference room", + "corridor", + "dining area", + "dining room", + "discussion area", + "furniture store", + "home office", + "kitchen", + "lab", + "lecture theatre", + "library", + "living room", + "office", + "rest space", + "study space", +] + + +def text_labels() -> List[str]: + """Return a list of labels.""" + return _LABELS + + +# from https://github.com/facebookresearch/omnivore/issues/12#issuecomment-1070911016 +sensor_to_params = { + "kv1": { + "baseline": 0.075, + }, + "kv1_b": { + "baseline": 0.075, + }, + "kv2": { + "baseline": 0.075, + }, + "realsense": { + "baseline": 0.095, + }, + "xtion": { + "baseline": 0.095, # guessed based on length of 18cm for ASUS xtion v1 + }, +} + + +def convert_depth_to_disparity( + depth_file: str, + intrinsics_file: str, + sensor_type: str, + min_depth: float = 0.01, + max_depth: int = 50, +) -> torch.Tensor: + """Load depth file and convert to disparity. + + Parameters + ---------- + depth_file : str + Path to the depth file. + intrinsics_file : str + Intrinsics_file is a txt file supplied in SUNRGBD with sensor information + Can be found at the path: os.path.join(root_dir, room_name, "intrinsics.txt") + sensor_type : str + Sensor type of the depth file. + min_depth : float, default=0.01 + Minimum depth value to clip the depth image. + max_depth : int, default=50 + Maximum depth value to clip the depth image. + + Returns + ------- + torch.Tensor + Disparity image from the depth image following the ImageBind implementation. + """ + with open(intrinsics_file, "r") as fh: + lines = fh.readlines() + focal_length = float(lines[0].strip().split()[0]) + baseline = sensor_to_params[sensor_type]["baseline"] + depth_image = np.array(Image.open(depth_file)) + depth = np.array(depth_image).astype(np.float32) + depth_in_meters = depth / 1000.0 + if min_depth is not None: + depth_in_meters = depth_in_meters.clip(min=min_depth, max=max_depth) + disparity = baseline * focal_length / depth_in_meters + return torch.from_numpy(disparity).float() + + +@store( + name="SUNRGBD", + group="datasets", + provider="mmlearn", + root_dir=os.getenv("SUNRGBD_ROOT_DIR", MISSING), +) +class SUNRGBDDataset(Dataset[Example]): + """SUN RGB-D dataset. + + Repo followed to extract the dataset: + https://github.com/TUI-NICR/nicr-scene-analysis-datasets + + Parameters + ---------- + root_dir : str + Path to the root directory of the dataset. + split : {"train", "test"}, default="train" + Split of the dataset to use. + return_type : {"disparity", "image"}, default="disparity" + Return type of the depth images. If "disparity", the depth images are + converted to disparity similar to the ImageBind implementation. + Else returns the depth image as a 3-channel image. + rgb_transform : Optional[Callable[[PILImage], torch.Tensor]], default=None + Transformation to apply to RGB images. + depth_transform : Optional[Callable[[PILImage], torch.Tensor]], default=None + Transformation to apply to depth images. + """ + + def __init__( + self, + root_dir: str, + split: Literal["train", "test"] = "train", + return_type: Literal["disparity", "image"] = "disparity", + rgb_transform: Optional[Callable[[PILImage], torch.Tensor]] = None, + depth_transform: Optional[Callable[[PILImage], torch.Tensor]] = None, + ) -> None: + super().__init__() + self._validate_args(root_dir, split, rgb_transform, depth_transform) + self.return_type = return_type + + self.root_dir = root_dir + with open(os.path.join(root_dir, f"{split}.txt"), "r") as f: + file_ids = f.readlines() + file_ids = [f.strip() for f in file_ids] + + root_dir = os.path.join(root_dir, split) + depth_files = [os.path.join(root_dir, "depth", f"{f}.png") for f in file_ids] + rgb_files = [os.path.join(root_dir, "rgb", f"{f}.jpg") for f in file_ids] + intrinsic_files = [ + os.path.join(root_dir, "intrinsics", f"{f}.txt") for f in file_ids + ] + + sensor_types = [ + file.removeprefix(os.path.join(root_dir, "depth")).split(os.sep)[1] + for file in depth_files + ] + + label_files = [ + os.path.join(root_dir, "scene_class", f"{f}.txt") for f in file_ids + ] + labels = [] + for label_file in label_files: + with open(label_file, "r") as file: # noqa: SIM115 + labels.append(file.read().strip()) + labels = [label.replace("_", " ") for label in labels] + labels = [ + _LABELS.index(label) if label in _LABELS else len(_LABELS) # type: ignore + for label in labels + ] + + # remove the samples with classes not in _LABELS + # this is to follow the same classes used in ImageBind + if split == "test": + valid_indices = [ + i + for i, label in enumerate(labels) + if label < len(_LABELS) # type: ignore + ] + rgb_files = [rgb_files[i] for i in valid_indices] + depth_files = [depth_files[i] for i in valid_indices] + labels = [labels[i] for i in valid_indices] + intrinsic_files = [intrinsic_files[i] for i in valid_indices] + sensor_types = [sensor_types[i] for i in valid_indices] + + self.samples = list( + zip(rgb_files, depth_files, labels, intrinsic_files, sensor_types) + ) + + self.rgb_transform = rgb_transform + self.depth_transform = depth_transform + + def __len__(self) -> int: + """Return the length of the dataset.""" + return len(self.samples) + + def _validate_args( + self, + root_dir: str, + split: str, + rgb_transform: Optional[Callable[[PILImage], torch.Tensor]], + depth_transform: Optional[Callable[[PILImage], torch.Tensor]], + ) -> None: + """Validate arguments.""" + if not os.path.isdir(root_dir): + raise NotADirectoryError( + f"The given `root_dir` {root_dir} is not a directory", + ) + if split not in ["train", "test"]: + raise ValueError( + f"Expected `split` to be one of `'train'` or `'test'`, but got {split}", + ) + if rgb_transform is not None and not callable(rgb_transform): + raise TypeError( + f"Expected argument `rgb_transform` to be callable, but got {type(rgb_transform)}", + ) + if depth_transform is not None and not callable(depth_transform): + raise TypeError( + f"Expected `depth_transform` to be callable, but got {type(depth_transform)}", + ) + + def __getitem__(self, idx: int) -> Example: + """Return RGB and depth images at index `idx`.""" + # Read images + rgb_image = cv2.imread(self.samples[idx][0], cv2.IMREAD_UNCHANGED) + if self.rgb_transform is not None: + rgb_image = self.rgb_transform(to_pil_image(rgb_image)) + + if self.return_type == "disparity": + depth_image = convert_depth_to_disparity( + self.samples[idx][1], + self.samples[idx][3], + self.samples[idx][4], + ) + else: + # Using cv2 instead of PIL Image since we use PNG grayscale images. + depth_image = cv2.imread( + self.samples[idx][1], + cv2.IMREAD_GRAYSCALE, + ) + # Make a 3-channel depth image to enable passing to a pretrained ViT. + depth_image = np.repeat(depth_image[:, :, np.newaxis], 3, axis=-1) + + if self.depth_transform is not None: + depth_image = self.depth_transform(to_pil_image(depth_image)) + + return Example( + { + Modalities.RGB: rgb_image, + Modalities.DEPTH: depth_image, + EXAMPLE_INDEX_KEY: idx, + Modalities.DEPTH.target: self.samples[idx][2], + } + ) diff --git a/mmlearn/hf_utils.py b/mmlearn/hf_utils.py new file mode 100644 index 0000000..c536202 --- /dev/null +++ b/mmlearn/hf_utils.py @@ -0,0 +1,75 @@ +"""Utilities for loading components from the HuggingFace `transformers` library.""" + +from typing import TYPE_CHECKING, Any, Dict, Optional, Type + +from lightning_utilities.core.imports import RequirementCache +from torch import nn +from transformers.models.auto.auto_factory import AutoConfig, _BaseAutoModelClass + + +_PEFT_AVAILABLE = RequirementCache("peft>=0.12.0") + + +if TYPE_CHECKING: + from peft import PeftConfig, PeftModel + + +def load_huggingface_model( + model_type: Type[_BaseAutoModelClass], + model_name_or_path: str, + load_pretrained_weights: bool = True, + get_model_attr: Optional[str] = None, + model_config_kwargs: Optional[Dict[str, Any]] = None, +) -> nn.Module: + """Load a model from the HuggingFace `transformers` library. + + Parameters + ---------- + model_type : Type[_BaseAutoModelClass] + The model class to instantiate e.g. `AutoModel`. + model_name_or_path : str + The model name or path to load the model from. + load_pretrained_weights : bool, optional, default=True + Whether to load the pretrained weights or not. If false, the argument + `pretrained_model_name_or_path` will be used to get the model configuration + and the model will be initialized with random weights. + get_model_attr : str, optional, default=None + If not None, the attribute of the model to return. For example, if the model + is an `AutoModel` and `get_model_attr='encoder'`, the encoder part of the + model will be returned. If None, the full model will be returned. + **model_config_kwargs : Dict[str, Any] + Additional keyword arguments to pass to the model configuration. + The values in kwargs of any keys which are configuration attributes will + be used to override the loaded values. Behavior concerning key/value pairs + whose keys are *not* configuration attributes is controlled by the + `return_unused_kwargs` keyword parameter. + + Returns + ------- + nn.Module + The instantiated model. + """ + model_config_kwargs = model_config_kwargs or {} + if load_pretrained_weights: + model = model_type.from_pretrained(model_name_or_path, **model_config_kwargs) + else: + config, kwargs = AutoConfig.from_pretrained( + pretrained_model_name_or_path=model_name_or_path, + return_unused_kwargs=True, + **model_config_kwargs, + ) + model = model_type.from_config(config, **kwargs) + + if get_model_attr is not None and hasattr(model, get_model_attr): + model = getattr(model, get_model_attr) + + return model + + +def _wrap_peft_model(model: nn.Module, peft_config: "PeftConfig") -> "PeftModel": + """Wrap the model with the `peft` library for parameter-efficient finetuning.""" + if not _PEFT_AVAILABLE: + raise ModuleNotFoundError(str(_PEFT_AVAILABLE)) + from peft import get_peft_model + + return get_peft_model(model, peft_config) diff --git a/mmlearn/modules/__init__.py b/mmlearn/modules/__init__.py new file mode 100644 index 0000000..96d4223 --- /dev/null +++ b/mmlearn/modules/__init__.py @@ -0,0 +1,3 @@ +"""Reusable components for building tasks.""" + +from mmlearn.modules.ema import ExponentialMovingAverage diff --git a/mmlearn/modules/ema.py b/mmlearn/modules/ema.py new file mode 100644 index 0000000..570b8ca --- /dev/null +++ b/mmlearn/modules/ema.py @@ -0,0 +1,146 @@ +"""Exponential Moving Average (EMA) module.""" + +import copy +from typing import Any, List, Optional, Set, Union + +import torch +from lightning.fabric.utilities import rank_zero_warn + + +class ExponentialMovingAverage: + """Exponential Moving Average (EMA) for the input 'model'. + + At each step the parameter of the EMA model is updates as the weighted average + of the model's parameters. Modified version of class `fairseq.models.ema.EMAModule`. + + Parameters + ---------- + model : nn.Module + The model to apply EMA to. + ema_decay : float + The initial decay value for EMA. + ema_end_decay : float + The final decay value for EMA. + ema_anneal_end_step : int + The number of steps to anneal the decay from `ema_decay` to `ema_end_decay`. + device_id : Optional[Union[int, torch.device]], optional, default=None + The device to move the model to. + skip_keys : Optional[Union[List[str], Set[str]]], optional, default=None + The keys to skip in the EMA update. These parameters will be copied directly + from the model to the EMA model.s + """ + + def __init__( + self, + model: torch.nn.Module, + ema_decay: float, + ema_end_decay: float, + ema_anneal_end_step: int, + device_id: Optional[Union[int, torch.device]] = None, + skip_keys: Optional[Union[List[str], Set[str]]] = None, + ): + self.model = self.deepcopy_model(model) + self.model.requires_grad_(False) + + if device_id is not None: + self.model.to(device_id) + + self.skip_keys: Union[List[str], set[str]] = skip_keys or set() + self.num_updates = 0 + self.decay = ema_decay # stores the current decay value + self.ema_decay = ema_decay + self.ema_end_decay = ema_end_decay + self.ema_anneal_end_step = ema_anneal_end_step + + @torch.no_grad() # type: ignore[misc] + def _update_weights(self, new_model: torch.nn.Module) -> None: + if self.decay < 1: + ema_state_dict = {} + ema_params = self.model.state_dict() + + for key, param in new_model.state_dict().items(): + ema_param = ema_params[key].float() + + if param.shape != ema_param.shape: + raise ValueError( + "Incompatible tensor shapes between student param and teacher param" + + "{} vs. {}".format(param.shape, ema_param.shape) + ) + + if key in self.skip_keys or not param.requires_grad: + ema_param = param.to(dtype=ema_param.dtype).clone() + else: + ema_param.mul_(self.decay) + ema_param.add_( + param.to(dtype=ema_param.dtype), + alpha=1 - self.decay, + ) + ema_state_dict[key] = ema_param + + self.model.load_state_dict(ema_state_dict, strict=False) + self.num_updates += 1 + else: + rank_zero_warn( + "Exponential Moving Average decay is 1.0, no update is applied to the model.", + stacklevel=1, + category=UserWarning, + ) + + def _update_ema_decay(self) -> None: + if self.ema_decay != self.ema_end_decay: + if self.num_updates >= self.ema_anneal_end_step: + decay = self.ema_end_decay + else: + decay = self.get_annealed_rate( + self.ema_decay, + self.ema_end_decay, + self.num_updates, + self.ema_anneal_end_step, + ) + self.decay = decay + + def step(self, new_model: torch.nn.Module) -> None: + """Perform single EMA update step.""" + self._update_weights(new_model) + self._update_ema_decay() + + @staticmethod + def deepcopy_model(model: torch.nn.Module) -> torch.nn.Module: + """Deep copy the model.""" + try: + return copy.deepcopy(model) + except RuntimeError as e: + raise RuntimeError("Unable to copy the model ", e) from e + + def restore(self, model: torch.nn.Module) -> torch.nn.Module: + """Reassign weights from another model. + + Parameters + ---------- + model : nn.Module + Model to load weights from. + + Returns + ------- + nn.Module + model with new weights + """ + d = self.model.state_dict() + model.load_state_dict(d, strict=False) + return model + + def state_dict(self) -> dict[str, Any]: + """Return the state dict of the model.""" + return self.model.state_dict() # type: ignore[no-any-return] + + @staticmethod + def get_annealed_rate( + start: float, + end: float, + curr_step: int, + total_steps: int, + ) -> float: + """Calculate EMA annealing rate.""" + r = end - start + pct_remaining = 1 - curr_step / total_steps + return end - r * pct_remaining diff --git a/mmlearn/modules/encoders/__init__.py b/mmlearn/modules/encoders/__init__.py new file mode 100644 index 0000000..0e1649d --- /dev/null +++ b/mmlearn/modules/encoders/__init__.py @@ -0,0 +1,20 @@ +"""Encoders.""" + +from mmlearn.modules.encoders.clip_encoders import ( + HFCLIPTextEncoder, + HFCLIPTextEncoderWithProjection, + HFCLIPVisionEncoder, + HFCLIPVisionEncoderWithProjection, + PubMedBERTForCLIPTextEncoding, +) +from mmlearn.modules.encoders.hf_text_encoders import HFTextEncoder + + +__all__ = [ + "HFTextEncoder", + "HFCLIPTextEncoder", + "HFCLIPTextEncoderWithProjection", + "HFCLIPVisionEncoder", + "HFCLIPVisionEncoderWithProjection", + "PubMedBERTForCLIPTextEncoding", +] diff --git a/mmlearn/modules/encoders/clip_encoders.py b/mmlearn/modules/encoders/clip_encoders.py new file mode 100644 index 0000000..d7b1970 --- /dev/null +++ b/mmlearn/modules/encoders/clip_encoders.py @@ -0,0 +1,658 @@ +"""Wrappers and interfaces for the CLIP models.""" + +from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Union + +import torch +import torch.distributed +import transformers +from hydra_zen import store +from lightning_utilities.core.rank_zero import rank_zero_warn +from torch import nn +from transformers.modeling_outputs import BaseModelOutput + +from mmlearn import hf_utils +from mmlearn.datasets.core import Modalities +from mmlearn.datasets.core.modalities import Modality +from mmlearn.modules.layers import PatchDropout + + +if TYPE_CHECKING: + from peft import PeftConfig + + +@store( + group="modules/encoders", + provider="mmlearn", + model_name_or_path="openai/clip-vit-base-patch16", +) +class HFCLIPTextEncoder(nn.Module): + """Wrapper around the `CLIPTextModel` from HuggingFace. + + Parameters + ---------- + model_name_or_path : str + The huggingface model name or a local path from which to load the model. + pretrained : bool, default=True + Whether to load the pretrained weights or not. + pooling_layer : nn.Module, optional, default=None + Pooling layer to apply to the last hidden state of the model. + freeze_layers : int | float | List[int] | bool, default=False + Whether to freeze layers of the model and which layers to freeze. If `True`, + all model layers are frozen. If it is an integer, the first `N` layers of + the model are frozen. If it is a float, the first `N` percent of the layers + are frozen. If it is a list of integers, the layers at the indices in the + list are frozen. + freeze_layer_norm : bool, default=True + Whether to freeze the layer normalization layers of the model. + peft_config : PeftConfig, optional, default=None + The configuration from the `peft` library to use to wrap the model + for parameter-efficient finetuning. + model_config_kwargs : Dict[str, Any], optional, default=None + Additional keyword arguments to pass to the model configuration. + + Warns + ----- + UserWarning + If both `peft_config` and `freeze_layers` are set. The `peft_config` will + override the `freeze_layers` setting. + + + """ + + def __init__( + self, + model_name_or_path: str, + pretrained: bool = True, + pooling_layer: Optional[nn.Module] = None, + freeze_layers: Union[int, float, List[int], bool] = False, + freeze_layer_norm: bool = True, + peft_config: Optional["PeftConfig"] = None, + model_config_kwargs: Optional[Dict[str, Any]] = None, + ) -> None: + """Initialize the CLIP text model.""" + super().__init__() + _warn_freeze_with_peft(peft_config, freeze_layers) + + model = hf_utils.load_huggingface_model( + transformers.CLIPTextModel, + model_name_or_path=model_name_or_path, + load_pretrained_weights=pretrained, + model_config_kwargs=model_config_kwargs, + ) + model = _freeze_text_model(model, freeze_layers, freeze_layer_norm) + if peft_config is not None: + model = hf_utils._wrap_peft_model(model, peft_config) + + self.model = model + self.pooling_layer = pooling_layer + + def forward(self, inputs: Dict[Union[str, Modality], Any]) -> BaseModelOutput: + """Run the forward pass. + + Parameters + ---------- + inputs : Dict[str | Modality, Any] + The input data. The `input_ids` will be expected under the `Modalities.TEXT` + key. + + Returns + ------- + BaseModelOutput + The output of the model, including the last hidden state, all hidden states, + and the attention weights, if `output_attentions` is set to `True`. + """ + outputs = self.model( + input_ids=inputs[Modalities.TEXT], + attention_mask=inputs.get("attention_mask"), + position_ids=inputs.get("position_ids"), + output_attentions=inputs.get("output_attentions"), + return_dict=True, + ) + last_hidden_state = outputs.hidden_states[-1] # NOTE: no layer norm applied + if self.pooling_layer: + last_hidden_state = self.pooling_layer(last_hidden_state) + + return BaseModelOutput( + last_hidden_state=last_hidden_state, + hidden_states=outputs.hidden_states, + attentions=outputs.attentions, + ) + + +@store( + group="modules/encoders", + provider="mmlearn", + model_name_or_path="openai/clip-vit-base-patch16", +) +class HFCLIPVisionEncoder(nn.Module): + """Wrapper around the `CLIPVisionModel` from HuggingFace. + + Parameters + ---------- + model_name_or_path : str + The huggingface model name or a local path from which to load the model. + pretrained : bool, default=True + Whether to load the pretrained weights or not. + pooling_layer : nn.Module, optional, default=None + Pooling layer to apply to the last hidden state of the model. + freeze_layers : int | float | List[int] | bool, default=False + Whether to freeze layers of the model and which layers to freeze. If `True`, + all model layers are frozen. If it is an integer, the first `N` layers of + the model are frozen. If it is a float, the first `N` percent of the layers + are frozen. If it is a list of integers, the layers at the indices in the + list are frozen. + freeze_layer_norm : bool, default=True + Whether to freeze the layer normalization layers of the model. + patch_dropout_rate : float, default=0.0 + The proportion of patch embeddings to drop out. + patch_dropout_shuffle : bool, default=False + Whether to shuffle the patches while applying patch dropout. + patch_dropout_bias : float, optional, default=None + The bias to apply to the patch dropout mask. + peft_config : PeftConfig, optional, default=None + The configuration from the `peft` library to use to wrap the model + for parameter-efficient finetuning. + model_config_kwargs : Dict[str, Any], optional, default=None + Additional keyword arguments to pass to the model configuration. + + Warns + ----- + UserWarning + If both `peft_config` and `freeze_layers` are set. The `peft_config` will + override the `freeze_layers` setting. + + """ + + def __init__( + self, + model_name_or_path: str, + pretrained: bool = True, + pooling_layer: Optional[nn.Module] = None, + freeze_layers: Union[int, float, List[int], bool] = False, + freeze_layer_norm: bool = True, + patch_dropout_rate: float = 0.0, + patch_dropout_shuffle: bool = False, + patch_dropout_bias: Optional[float] = None, + peft_config: Optional["PeftConfig"] = None, + model_config_kwargs: Optional[Dict[str, Any]] = None, + ) -> None: + """Initialize the CLIP vision model.""" + super().__init__() + _warn_freeze_with_peft(peft_config, freeze_layers) + + model = hf_utils.load_huggingface_model( + transformers.CLIPVisionModel, + model_name_or_path=model_name_or_path, + load_pretrained_weights=pretrained, + model_config_kwargs=model_config_kwargs, + ) + model = _freeze_vision_model(model, freeze_layers, freeze_layer_norm) + if peft_config is not None: + model = hf_utils._wrap_peft_model(model, peft_config) + + self.model = model.vision_model + self.pooling_layer = pooling_layer + self.patch_dropout = None + if patch_dropout_rate > 0: + self.patch_dropout = PatchDropout( + keep_rate=1 - patch_dropout_rate, + token_shuffling=patch_dropout_shuffle, + bias=patch_dropout_bias, + ) + + def forward(self, inputs: Dict[Union[str, Modality], Any]) -> BaseModelOutput: + """Run the forward pass. + + Parameters + ---------- + inputs : Dict[str | Modality, Any] + The input data. The image tensor will be expected under the `Modalities.RGB` + key. + + Returns + ------- + BaseModelOutput + The output of the model, including the last hidden state, all hidden states, + and the attention weights, if `output_attentions` is set to `True`. + + """ + # FIXME: handle other vision modalities + pixel_values = inputs[Modalities.RGB] + hidden_states = self.model.embeddings(pixel_values) + if self.patch_dropout is not None: + hidden_states = self.patch_dropout(hidden_states) + hidden_states = self.model.pre_layrnorm(hidden_states) + + encoder_outputs = self.model.encoder( + inputs_embeds=hidden_states, + output_attentions=inputs.get( + "output_attentions", self.model.config.output_attentions + ), + output_hidden_states=True, + return_dict=True, + ) + + last_hidden_state = encoder_outputs[0] + if self.pooling_layer is not None: + last_hidden_state = self.pooling_layer(last_hidden_state) + + return BaseModelOutput( + last_hidden_state=last_hidden_state, + hidden_states=encoder_outputs.hidden_states, + attentions=encoder_outputs.attentions, + ) + + +@store( + group="modules/encoders", + provider="mmlearn", + model_name_or_path="openai/clip-vit-base-patch16", +) +class HFCLIPTextEncoderWithProjection(nn.Module): + """Wrapper around the `CLIPTextModelWithProjection` from HuggingFace. + + Parameters + ---------- + model_name_or_path : str + The huggingface model name or a local path from which to load the model. + pretrained : bool, default=True + Whether to load the pretrained weights or not. + use_all_token_embeddings : bool, default=False + Whether to use all token embeddings for the text. If `False` the first token + embedding will be used. + freeze_layers : int | float | List[int] | bool, default=False + Whether to freeze layers of the model and which layers to freeze. If `True`, + all model layers are frozen. If it is an integer, the first `N` layers of + the model are frozen. If it is a float, the first `N` percent of the layers + are frozen. If it is a list of integers, the layers at the indices in the + list are frozen. + freeze_layer_norm : bool, default=True + Whether to freeze the layer normalization layers of the model. + peft_config : PeftConfig, optional, default=None + The configuration from the `peft` library to use to wrap the model + for parameter-efficient finetuning. + + Warns + ----- + UserWarning + If both `peft_config` and `freeze_layers` are set. The `peft_config` will + override the `freeze_layers` setting. + + """ + + def __init__( + self, + model_name_or_path: str, + pretrained: bool = True, + use_all_token_embeddings: bool = False, + freeze_layers: Union[int, float, List[int], bool] = False, + freeze_layer_norm: bool = True, + peft_config: Optional["PeftConfig"] = None, + model_config_kwargs: Optional[Dict[str, Any]] = None, + ) -> None: + """Initialize the model.""" + super().__init__() + _warn_freeze_with_peft(peft_config, freeze_layers) + + self.use_all_token_embeddings = use_all_token_embeddings + model = hf_utils.load_huggingface_model( + transformers.CLIPTextModelWithProjection, + model_name_or_path=model_name_or_path, + load_pretrained_weights=pretrained, + model_config_kwargs=model_config_kwargs, + ) + + model = _freeze_text_model(model, freeze_layers, freeze_layer_norm) + if peft_config is not None: + model = hf_utils._wrap_peft_model(model, peft_config) + + self.model = model + + def forward(self, inputs: Dict[Union[str, Modality], Any]) -> Tuple[torch.Tensor]: + """Run the forward pass. + + Parameters + ---------- + inputs : Dict[str | Modality, Any] + The input data. The `input_ids` will be expected under the `Modalities.TEXT` + key. + + Returns + ------- + Tuple[torch.Tensor] + The text embeddings. Will be a tuple with a single element. + """ + input_ids = inputs[Modalities.TEXT] + attention_mask = inputs.get("attention_mask") + position_ids = inputs.get("position_ids") + + if self.use_all_token_embeddings: + text_outputs = self.model.text_model( + input_ids=input_ids, + attention_mask=attention_mask, + position_ids=position_ids, + return_dict=True, + ) + # TODO: add more options for pooling before projection + text_embeds = self.model.text_projection(text_outputs.last_hidden_state) + else: + text_embeds = self.model( + input_ids=input_ids, + attention_mask=attention_mask, + position_ids=position_ids, + return_dict=True, + ).text_embeds + + return (text_embeds,) + + +@store( + group="modules/encoders", + provider="mmlearn", + model_name_or_path="openai/clip-vit-base-patch16", +) +class HFCLIPVisionEncoderWithProjection(nn.Module): + """Wrapper around the `CLIPVisionModelWithProjection` class from HuggingFace. + + Parameters + ---------- + model_name_or_path : str + The huggingface model name or a local path from which to load the model. + pretrained : bool, default=True + Whether to load the pretrained weights or not. + use_all_token_embeddings : bool, default=False + Whether to use all token embeddings for the text. If `False` the first token + embedding will be used. + freeze_layers : int | float | List[int] | bool, default=False + Whether to freeze layers of the model and which layers to freeze. If `True`, + all model layers are frozen. If it is an integer, the first `N` layers of + the model are frozen. If it is a float, the first `N` percent of the layers + are frozen. If it is a list of integers, the layers at the indices in the + list are frozen. + freeze_layer_norm : bool, default=True + Whether to freeze the layer normalization layers of the model. + patch_dropout_rate : float, default=0.0 + The proportion of patch embeddings to drop out. + patch_dropout_shuffle : bool, default=False + Whether to shuffle the patches while applying patch dropout. + patch_dropout_bias : float, optional, default=None + The bias to apply to the patch dropout mask. + peft_config : PeftConfig, optional, default=None + The configuration from the `peft` library to use to wrap the model + for parameter-efficient finetuning. + model_config_kwargs : Dict[str, Any], optional, default=None + Additional keyword arguments to pass to the model configuration. + + Warns + ----- + UserWarning + If both `peft_config` and `freeze_layers` are set. The `peft_config` will + override the `freeze_layers` setting. + + """ + + def __init__( + self, + model_name_or_path: str, + pretrained: bool = True, + use_all_token_embeddings: bool = False, + patch_dropout_rate: float = 0.0, + patch_dropout_shuffle: bool = False, + patch_dropout_bias: Optional[float] = None, + freeze_layers: Union[int, float, List[int], bool] = False, + freeze_layer_norm: bool = True, + peft_config: Optional["PeftConfig"] = None, + model_config_kwargs: Optional[Dict[str, Any]] = None, + ) -> None: + """Initialize the model.""" + super().__init__() + _warn_freeze_with_peft(peft_config, freeze_layers) + + self.use_all_token_embeddings = use_all_token_embeddings + model = hf_utils.load_huggingface_model( + transformers.CLIPVisionModelWithProjection, + model_name_or_path=model_name_or_path, + load_pretrained_weights=pretrained, + model_config_kwargs=model_config_kwargs, + ) + + model = _freeze_vision_model(model, freeze_layers, freeze_layer_norm) + if peft_config is not None: + model = hf_utils._wrap_peft_model(model, peft_config) + + self.model = model + self.patch_dropout = None + if patch_dropout_rate > 0: + self.patch_dropout = PatchDropout( + keep_rate=1 - patch_dropout_rate, + token_shuffling=patch_dropout_shuffle, + bias=patch_dropout_bias, + ) + + def forward(self, inputs: Dict[Union[str, Modality], Any]) -> Tuple[torch.Tensor]: + """Run the forward pass. + + Parameters + ---------- + inputs : Dict[str | Modality, Any] + The input data. The image tensor will be expected under the `Modalities.RGB` + key. + + Returns + ------- + Tuple[torch.Tensor] + The image embeddings. Will be a tuple with a single element. + """ + pixel_values = inputs[Modalities.RGB] + hidden_states = self.model.vision_model.embeddings(pixel_values) + if self.patch_dropout is not None: + hidden_states = self.patch_dropout(hidden_states) + hidden_states = self.model.vision_model.pre_layrnorm(hidden_states) + + encoder_outputs = self.model.vision_model.encoder( + inputs_embeds=hidden_states, return_dict=True + ) + + last_hidden_state = encoder_outputs.last_hidden_state + if self.use_all_token_embeddings: + pooled_output = last_hidden_state + else: + pooled_output = last_hidden_state[:, 0, :] + pooled_output = self.model.vision_model.post_layernorm(pooled_output) + + return (self.model.visual_projection(pooled_output),) + + +@store(group="modules/encoders", provider="mmlearn") +class PubMedBERTForCLIPTextEncoding(nn.Module): + """BiomedNLP's PubMedBERT model for CLIP text encoding. + + This module is wrapper around the PubMedBERT model from huggingface. + + Parameters + ---------- + pretrained : bool, default=False + Whether to load the pretrained weights or not. + pooling_layer : nn.Module, optional, default=None + Pooling layer to apply to the last hidden state of the model. + freeze_layers : int | float | List[int] | bool, default=False + Whether to freeze layers of the model and which layers to freeze. If `True`, + all model layers are frozen. If it is an integer, the first `N` layers of + the model are frozen. If it is a float, the first `N` percent of the layers + are frozen. If it is a list of integers, the layers at the indices in the + list are frozen. + freeze_layer_norm : bool, default=True + Whether to freeze the layer normalization layers of the model. + peft_config : PeftConfig, optional, default=None + The configuration from the `peft` library to use to wrap the model + for parameter-efficient finetuning. + model_config_kwargs : Dict[str, Any], optional, default=None + Additional keyword arguments to pass to the model configuration. + + Warns + ----- + UserWarning + If both `peft_config` and `freeze_layers` are set. The `peft_config` will + override the `freeze_layers` setting. + + """ + + def __init__( + self, + pretrained: bool = True, + pooling_layer: Optional[nn.Module] = None, + freeze_layers: Union[int, float, List[int], bool] = False, + freeze_layer_norm: bool = True, + peft_config: Optional["PeftConfig"] = None, + model_config_kwargs: Optional[Dict[str, Any]] = None, + ) -> None: + """Initialize the model.""" + super().__init__() + _warn_freeze_with_peft(peft_config, freeze_layers) + + model = hf_utils.load_huggingface_model( + transformers.AutoModelForMaskedLM, + "microsoft/BiomedNLP-BiomedBERT-base-uncased-abstract-fulltext", + load_pretrained_weights=pretrained, + get_model_attr="bert", + model_config_kwargs=model_config_kwargs, + ) + + if isinstance(freeze_layers, bool) and freeze_layers: + for name, param in model.named_parameters(): + param.requires_grad = ( + (not freeze_layer_norm) if "LayerNorm" in name else False + ) + + layers = [model.embeddings, *model.encoder.layer] + if isinstance(freeze_layers, float): + freeze_layers = int(freeze_layers * len(layers)) + if isinstance(freeze_layers, int): + freeze_layers = list(range(freeze_layers)) + + if isinstance(freeze_layers, list): + for idx, layer in enumerate(layers): + if idx in freeze_layers: + for name, param in layer.named_parameters(): + param.requires_grad = ( + (not freeze_layer_norm) if "LayerNorm" in name else False + ) + + if peft_config is not None: + model = hf_utils._wrap_peft_model(model, peft_config) + + self.model = model + self.pooling_layer = pooling_layer + + def forward(self, inputs: Dict[Union[str, Modality], Any]) -> BaseModelOutput: + """Run the forward pass. + + Parameters + ---------- + inputs : Dict[str | Modality, Any] + The input data. The `input_ids` will be expected under the `Modalities.TEXT` + key. + + Returns + ------- + BaseModelOutput + The output of the model, including the last hidden state, all hidden states, + and the attention weights, if `output_attentions` is set to `True`. + """ + output = self.model( + input_ids=inputs[Modalities.TEXT], + attention_mask=inputs.get("attention_mask"), + inputs_embeds=inputs.get("inputs_embeds"), + output_attentions=inputs.get("output_attentions"), + output_hidden_states=True, + return_dict=True, + ) + last_hidden_state = output.last_hidden_state + if self.pooling_layer is not None: + last_hidden_state = self.pooling_layer(last_hidden_state) + + return BaseModelOutput( + last_hidden_state=last_hidden_state, + hidden_states=output.hidden_states, + attentions=output.attentions, + ) + + +#### Utility methods #### + + +def _freeze_text_model( + model: nn.Module, + freeze_layers: Union[int, float, List[int], bool], + freeze_layer_norm: bool, +) -> nn.Module: + """Freeze the layers of a huggingface clip text model.""" + if isinstance(freeze_layers, bool) and freeze_layers: + for name, param in model.text_model.named_parameters(): + param.requires_grad = ( + (not freeze_layer_norm) if "LayerNorm" in name else False + ) + + layers = [ # NOTE: projection layer is not included + model.text_model.embeddings, + *model.text_model.encoder.layers, + model.text_model.final_layer_norm, + ] + if isinstance(freeze_layers, float): + freeze_layers = int(freeze_layers * len(layers)) + if isinstance(freeze_layers, int): + freeze_layers = list(range(freeze_layers)) + + if isinstance(freeze_layers, list): + for idx, layer in enumerate(layers): + if idx in freeze_layers: + for name, param in layer.named_parameters(): + param.requires_grad = ( + (not freeze_layer_norm) if "LayerNorm" in name else False + ) + return model + + +def _freeze_vision_model( + model: nn.Module, + freeze_layers: Union[int, float, List[int], bool], + freeze_layer_norm: bool, +) -> nn.Module: + """Freeze the layers of a huggingface clip vision model.""" + if isinstance(freeze_layers, bool) and freeze_layers: + for name, param in model.vision_model.named_parameters(): + param.requires_grad = ( + (not freeze_layer_norm) if "LayerNorm" in name else False + ) + + layers = [ # NOTE: projection layer is not included + model.vision_model.embeddings, + model.vision_model.pre_layrnorm, + *model.vision_model.encoder.layers, + model.vision_model.post_layernorm, + ] + if isinstance(freeze_layers, float): + freeze_layers = int(freeze_layers * len(layers)) + if isinstance(freeze_layers, int): + freeze_layers = list(range(freeze_layers)) + + if isinstance(freeze_layers, list): + for idx, layer in enumerate(layers): + if idx in freeze_layers: + for name, param in layer.named_parameters(): + param.requires_grad = ( + (not freeze_layer_norm) if "LayerNorm" in name else False + ) + return model + + +def _warn_freeze_with_peft( + peft_config: Optional["PeftConfig"], freeze_layers: Any +) -> None: + """Raise a warning if both `peft_config` and `freeze_layers` are set.""" + if peft_config is not None and freeze_layers: + rank_zero_warn( + "Setting both `peft_config` and `freeze_layers` is not recommended. " + "The `peft_config` will override the `freeze_layers` setting.", + category=UserWarning, + ) diff --git a/mmlearn/modules/encoders/hf_text_encoders.py b/mmlearn/modules/encoders/hf_text_encoders.py new file mode 100644 index 0000000..c83e8a9 --- /dev/null +++ b/mmlearn/modules/encoders/hf_text_encoders.py @@ -0,0 +1,173 @@ +"""Huggingface text encoder model.""" + +from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union + +from hydra_zen import store +from lightning_utilities.core.rank_zero import rank_zero_warn +from torch import nn +from transformers import AutoModelForTextEncoding +from transformers.modeling_outputs import BaseModelOutput + +from mmlearn import hf_utils +from mmlearn.datasets.core import Modalities +from mmlearn.datasets.core.modalities import Modality + + +if TYPE_CHECKING: + from peft import PeftConfig + + +@store(group="modules/encoders", provider="mmlearn") +class HFTextEncoder(nn.Module): + """Wrapper around huggingface models in the `AutoModelForTextEncoding` class. + + Parameters + ---------- + model_name_or_path : str + The huggingface model name or a local path from which to load the model. + pretrained : bool, default=True + Whether to load the pretrained weights or not. + pooling_layer : nn.Module, optional, default=None + Pooling layer to apply to the last hidden state of the model. + freeze_layers : int | float | List[int] | bool, default=False + Whether to freeze layers of the model and which layers to freeze. If `True`, + all model layers are frozen. If it is an integer, the first `N` layers of + the model are frozen. If it is a float, the first `N` percent of the layers + are frozen. If it is a list of integers, the layers at the indices in the + list are frozen. + freeze_layer_norm : bool, default=True + Whether to freeze the layer normalization layers of the model. + peft_config : PeftConfig, optional, default=None + The configuration from the `peft` library to use to wrap the model + for parameter-efficient finetuning. + model_config_kwargs : Dict[str, Any], optional, default=None + Additional keyword arguments to pass to the model configuration. + + Warns + ----- + UserWarning + If both `peft_config` and `freeze_layers` are set. The `peft_config` will + override the `freeze_layers` setting. + + + """ + + def __init__( # noqa: PLR0912 + self, + model_name_or_path: str, + pretrained: bool = True, + pooling_layer: Optional[nn.Module] = None, + freeze_layers: Union[int, float, List[int], bool] = False, + freeze_layer_norm: bool = True, + peft_config: Optional["PeftConfig"] = None, + model_config_kwargs: Optional[Dict[str, Any]] = None, + ): + """Initialize the model.""" + super().__init__() + if model_config_kwargs is None: + model_config_kwargs = {} + model_config_kwargs["use_return_dict"] = True + model_config_kwargs["output_hidden_states"] = True + model_config_kwargs["add_pooling_layer"] = False + model = hf_utils.load_huggingface_model( + AutoModelForTextEncoding, + model_name_or_path, + load_pretrained_weights=pretrained, + model_config_kwargs=model_config_kwargs, + ) + if hasattr(model.config, "is_decoder") and model.config.is_decoder: + raise ValueError("Model is a decoder. Only encoder models are supported.") + + if not pretrained and freeze_layers: + rank_zero_warn( + "Freezing layers when loading a model with random weights may lead to " + "unexpected behavior. Consider setting `freeze_layers=False` if " + "`pretrained=False`.", + ) + + if isinstance(freeze_layers, bool) and freeze_layers: + for name, param in model.named_parameters(): + param.requires_grad = ( + (not freeze_layer_norm) if "LayerNorm" in name else False + ) + + if isinstance( + freeze_layers, (float, int, list) + ) and model.config.model_type in ["flaubert", "xlm"]: + # flaubert and xlm models have a different architecture that does not + # support freezing individual layers in the same way as other models + raise ValueError( + f"Freezing individual layers is not supported for {model.config.model_type} " + "models. Please use `freeze_layers=False` or `freeze_layers=True`." + ) + + # get list of layers + embeddings = model.embeddings + encoder = getattr(model, "encoder", None) or getattr( + model, "transformer", model + ) + encoder_layers = ( + getattr(encoder, "layer", None) + or getattr(encoder, "layers", None) + or getattr(encoder, "block", None) + ) + if encoder_layers is None and hasattr(encoder, "albert_layer_groups"): + encoder_layers = [ + layer + for group in encoder.albert_layer_groups + for layer in group.albert_layers + ] + modules = [embeddings] + if encoder_layers is not None and isinstance(encoder_layers, list): + modules.extend(encoder_layers) + + if isinstance(freeze_layers, float): + freeze_layers = int(freeze_layers * len(modules)) + if isinstance(freeze_layers, int): + freeze_layers = list(range(freeze_layers)) + + if isinstance(freeze_layers, list): + for idx, module in enumerate(modules): + if idx in freeze_layers: + for name, param in module.named_parameters(): + param.requires_grad = ( + (not freeze_layer_norm) if "LayerNorm" in name else False + ) + + if peft_config is not None: + model = hf_utils._wrap_peft_model(model, peft_config) + + self.model = model + self.pooling_layer = pooling_layer + + def forward(self, inputs: Dict[Union[str, Modality], Any]) -> BaseModelOutput: + """Run the forward pass. + + Parameters + ---------- + inputs : Dict[str | Modality, Any] + The input data. The `input_ids` will be expected under the `Modalities.TEXT` + key. + + Returns + ------- + BaseModelOutput + The output of the model, including the last hidden state, all hidden states, + and the attention weights, if `output_attentions` is set to `True`. + """ + outputs = self.model( + input_ids=inputs[Modalities.TEXT], + attention_mask=inputs.get("attention_mask"), + position_ids=inputs.get("position_ids"), + output_attentions=inputs.get("output_attentions"), + return_dict=True, + ) + last_hidden_state = outputs.hidden_states[-1] # NOTE: no layer norm applied + if self.pooling_layer: + last_hidden_state = self.pooling_layer(last_hidden_state) + + return BaseModelOutput( + last_hidden_state=last_hidden_state, + hidden_states=outputs.hidden_states, + attentions=outputs.attentions, + ) diff --git a/mmlearn/modules/layers/__init__.py b/mmlearn/modules/layers/__init__.py new file mode 100644 index 0000000..85b3dff --- /dev/null +++ b/mmlearn/modules/layers/__init__.py @@ -0,0 +1,9 @@ +"""Custom, reusable layers for models and tasks.""" + +from mmlearn.modules.layers.logit_scaling import LearnableLogitScaling +from mmlearn.modules.layers.mlp import MLP +from mmlearn.modules.layers.normalization import L2Norm +from mmlearn.modules.layers.patch_dropout import PatchDropout + + +__all__ = ["L2Norm", "LearnableLogitScaling", "PatchDropout", "MLP"] diff --git a/mmlearn/modules/layers/logit_scaling.py b/mmlearn/modules/layers/logit_scaling.py new file mode 100644 index 0000000..ac7f7e5 --- /dev/null +++ b/mmlearn/modules/layers/logit_scaling.py @@ -0,0 +1,54 @@ +"""Learnable logit scaling layer.""" + +import numpy as np +import torch +from hydra_zen import store + + +# modified from: https://github.com/facebookresearch/ImageBind/blob/main/imagebind/models/helpers.py +@store(group="modules/layers", provider="mmlearn") +class LearnableLogitScaling(torch.nn.Module): + """Logit scaling layer. + + Parameters + ---------- + logit_scale_init : float, optional, default=1/0.07 + Initial value of the logit scale. + learnable : bool, optional, default=True + If True, the logit scale is learnable. Otherwise, it is fixed. + max_logit_scale : float, optional, default=100 + Maximum value of the logit scale. + """ + + def __init__( + self, + logit_scale_init: float = 1 / 0.07, + learnable: bool = True, + max_logit_scale: float = 100, + ) -> None: + super().__init__() + self.max_logit_scale = max_logit_scale + self.logit_scale_init = logit_scale_init + self.learnable = learnable + log_logit_scale = torch.ones([]) * np.log(self.logit_scale_init) + if learnable: + self.log_logit_scale = torch.nn.Parameter(log_logit_scale) + else: + self.register_buffer("log_logit_scale", log_logit_scale) + + def forward(self, x: torch.Tensor) -> torch.Tensor: + """Apply scaling to the input tensor. + + Parameters + ---------- + x : torch.Tensor + Input tensor of shape (batch_sz, seq_len, dim). + """ + return torch.clip(self.log_logit_scale.exp(), max=self.max_logit_scale) * x + + def extra_repr(self) -> str: + """Return the string representation of the layer.""" + return ( + f"logit_scale_init={self.logit_scale_init},learnable={self.learnable}," + f" max_logit_scale={self.max_logit_scale}" + ) diff --git a/mmlearn/modules/layers/mlp.py b/mmlearn/modules/layers/mlp.py new file mode 100644 index 0000000..ff93853 --- /dev/null +++ b/mmlearn/modules/layers/mlp.py @@ -0,0 +1,117 @@ +"""Multi-layer perceptron (MLP).""" + +from typing import Callable, List, Optional, Union + +import torch +from hydra_zen import store + + +@store(group="modules/layers", provider="mmlearn") +class MLP(torch.nn.Sequential): + """Multi-layer perceptron (MLP). + + This module will create a block of Linear -> Normalization -> Activation -> Dropout + layers. + + Parameters + ---------- + in_dim : int + The input dimension. + out_dim : int, optional, default=None + The output dimension. If not specified, it is set to `in_dim`. + hidden_dims : list, optional, default=None + The dimensions of the hidden layers. The length of the list determines the + number of hidden layers. This parameter is mutually exclusive with + `hidden_dims_multiplier`. + hidden_dims_multiplier : list, optional, default=None + The multipliers to apply to the input dimension to get the dimensions of + the hidden layers. The length of the list determines the number of hidden + layers. The multipliers will be used to get the dimensions of the hidden + layers. This parameter is mutually exclusive with `hidden_dims`. + apply_multiplier_to_in_dim : bool, optional, default=False + Whether to apply the `hidden_dims_multiplier` to `in_dim` to get the + dimensions of the hidden layers. If `False`, the multipliers will be applied + to the dimensions of the previous hidden layer, starting from `in_dim`. + This parameter is only relevant when `hidden_dims_multiplier` is specified. + norm_layer : callable, optional, default=None + The normalization layer to use. If not specified, no normalization is used. + Partial functions can be used to specify the normalization layer with specific + parameters. + activation_layer : callable, optional, default=torch.nn.ReLU + The activation layer to use. If not specified, ReLU is used. Partial functions + can be used to specify the activation layer with specific parameters. + bias : bool, optional, default=True + Whether to use bias in the linear layers. + dropout : float, optional, default=0.0 + The dropout probability to use. + + """ + + def __init__( # noqa: PLR0912 + self, + in_dim: int, + out_dim: Optional[int] = None, + hidden_dims: Optional[List[int]] = None, + hidden_dims_multiplier: Optional[List[float]] = None, + apply_multiplier_to_in_dim: bool = False, + norm_layer: Optional[Callable[..., torch.nn.Module]] = None, + activation_layer: Optional[Callable[..., torch.nn.Module]] = torch.nn.ReLU, + bias: Union[bool, List[bool]] = True, + dropout: Union[float, List[float]] = 0.0, + ) -> None: + """Initialize the MLP module.""" + if hidden_dims is None and hidden_dims_multiplier is None: + hidden_dims = [] + if hidden_dims is not None and hidden_dims_multiplier is not None: + raise ValueError( + "Only one of `hidden_dims` or `hidden_dims_multiplier` must be specified." + ) + + if hidden_dims is None and hidden_dims_multiplier is not None: + if apply_multiplier_to_in_dim: + hidden_dims = [ + int(in_dim * multiplier) for multiplier in hidden_dims_multiplier + ] + else: + hidden_dims = [int(in_dim * hidden_dims_multiplier[0])] + for multiplier in hidden_dims_multiplier[1:]: + hidden_dims.append(int(hidden_dims[-1] * multiplier)) + + if isinstance(bias, bool): + bias_list: List[bool] = [bias] * (len(hidden_dims) + 1) # type: ignore[arg-type] + else: + bias_list = bias + if len(bias_list) != len(hidden_dims) + 1: # type: ignore[arg-type] + raise ValueError( + "Expected `bias` to be a boolean or a list of booleans with length " + "equal to the number of linear layers in the MLP." + ) + + if isinstance(dropout, float): + dropout_list: List[float] = [dropout] * len(hidden_dims) # type: ignore[arg-type] + else: + dropout_list = dropout + if len(dropout_list) != len(hidden_dims): # type: ignore[arg-type] + raise ValueError( + "Expected `dropout` to be a float or a list of floats with length " + "equal to the number of dropout layers in the MLP." + ) + + layers = [] + for layer_idx, hidden_dim in enumerate(hidden_dims[:-1]): # type: ignore[index] + layers.append( + torch.nn.Linear(in_dim, hidden_dim, bias=bias_list[layer_idx]) + ) + if norm_layer is not None: + layers.append(norm_layer(hidden_dim)) + if activation_layer is not None: + layers.append(activation_layer()) + layers.append(torch.nn.Dropout(dropout_list[layer_idx])) + in_dim = hidden_dim + + if out_dim is None: + out_dim = in_dim + + layers.append(torch.nn.Linear(in_dim, out_dim, bias=bias_list[-1])) + + super().__init__(*layers) diff --git a/mmlearn/modules/layers/normalization.py b/mmlearn/modules/layers/normalization.py new file mode 100644 index 0000000..48e167c --- /dev/null +++ b/mmlearn/modules/layers/normalization.py @@ -0,0 +1,34 @@ +"""Normalization layers.""" + +import torch +from hydra_zen import store + + +@store(group="modules/layers", provider="mmlearn") +class L2Norm(torch.nn.Module): + """L2 normalization module. + + Parameters + ---------- + dim : int + The dimension along which to normalize. + """ + + def __init__(self, dim: int) -> None: + super().__init__() + self.dim = dim + + def forward(self, x: torch.Tensor) -> torch.Tensor: + """Apply L2 normalization to the input tensor. + + Parameters + ---------- + x : torch.Tensor + Input tensor of shape (batch_sz, seq_len, dim). + + Returns + ------- + torch.Tensor + Normalized tensor of shape (batch_sz, seq_len, dim). + """ + return torch.nn.functional.normalize(x, dim=self.dim, p=2) diff --git a/mmlearn/modules/layers/patch_dropout.py b/mmlearn/modules/layers/patch_dropout.py new file mode 100644 index 0000000..3ca30c0 --- /dev/null +++ b/mmlearn/modules/layers/patch_dropout.py @@ -0,0 +1,97 @@ +"""Patch dropout layer.""" + +from typing import Optional + +import torch + + +# modified from: https://github.com/yueliukth/PatchDropout/blob/main/scripts/patchdropout.py +class PatchDropout(torch.nn.Module): + """Patch dropout layer. + + Drops patch tokens (after embedding and adding CLS token) from the input tensor. + Usually used in vision transformers to reduce the number of tokens [1]. + + Parameters + ---------- + keep_rate : float, optional, default=0.5 + The proportion of tokens to keep. + bias : float, optional, default=None + The bias to add to the random noise before sorting. + token_shuffling : bool, optional, default=False + If True, the tokens are shuffled. + + References + ---------- + [1] PatchDropout: Economizing Vision Transformers Using Patch Dropout + https://arxiv.org/abs/2208.07220 + """ + + def __init__( + self, + keep_rate: float = 0.5, + bias: Optional[float] = None, + token_shuffling: bool = False, + ): + super().__init__() + assert 0 < keep_rate <= 1, "The keep_rate must be in (0,1]" + + self.bias = bias + self.keep_rate = keep_rate + self.token_shuffling = token_shuffling + + def forward(self, x: torch.Tensor, force_drop: bool = False) -> torch.Tensor: + """Drop tokens from the input tensor. + + Parameters + ---------- + x : torch.Tensor + Input tensor of shape (batch_sz, seq_len, dim). + force_drop : bool, optional, default=False + If True, the tokens are always dropped, even when the model is in + evaluation mode. + + Returns + ------- + torch.Tensor + Tensor of shape (batch_sz, keep_len, dim) containing the kept tokens. + """ + if (not self.training and not force_drop) or self.keep_rate == 1: + return x + + batch_sz, _, dim = x.shape + + cls_mask = torch.zeros( # assumes that CLS is always the 1st element + batch_sz, 1, dtype=torch.int64, device=x.device + ) + patch_mask = self.uniform_mask(x) + patch_mask = torch.hstack([cls_mask, patch_mask]) + + return torch.gather(x, dim=1, index=patch_mask.unsqueeze(-1).repeat(1, 1, dim)) + + def uniform_mask(self, x: torch.Tensor) -> torch.Tensor: + """Generate token ids to keep from uniform random distribution. + + Parameters + ---------- + x : torch.Tensor + Input tensor of shape (batch_sz, seq_len, dim). + + Returns + ------- + torch.Tensor + Tensor of shape (batch_sz, keep_len) containing the token ids to keep. + + """ + batch_sz, seq_len, _ = x.shape + seq_len = seq_len - 1 # patch length (without CLS) + + keep_len = int(seq_len * self.keep_rate) + noise = torch.rand(batch_sz, seq_len, device=x.device) + if self.bias is not None: + noise += self.bias + ids = torch.argsort(noise, dim=1) + keep_ids = ids[:, :keep_len] + if not self.token_shuffling: + keep_ids = keep_ids.sort(1)[0] + return keep_ids diff --git a/mmlearn/modules/losses/__init__.py b/mmlearn/modules/losses/__init__.py new file mode 100644 index 0000000..65c1b3d --- /dev/null +++ b/mmlearn/modules/losses/__init__.py @@ -0,0 +1,6 @@ +"""Loss functions.""" + +from mmlearn.modules.losses.contrastive_loss import CLIPLoss + + +__all__ = ["CLIPLoss"] diff --git a/mmlearn/modules/losses/contrastive_loss.py b/mmlearn/modules/losses/contrastive_loss.py new file mode 100644 index 0000000..9ad2a75 --- /dev/null +++ b/mmlearn/modules/losses/contrastive_loss.py @@ -0,0 +1,203 @@ +"""Implementations of the contrastive loss and its variants.""" + +from typing import Dict, Tuple + +import torch +import torch.distributed as dist +from hydra_zen import store +from torch import nn +from torch.nn import functional as F # noqa: N812 +from torchmetrics.utilities.compute import _safe_matmul +from torchmetrics.utilities.distributed import gather_all_tensors + + +@store(group="modules/losses", provider="mmlearn") +class CLIPLoss(nn.Module): + """CLIP Loss module. + + Parameters + ---------- + l2_normalize : bool, default=False + Whether to L2 normalize the features. + local_loss : bool, default=False + Whether to calculate the loss locally i.e. `local_features@global_features`. + gather_with_grad : bool, default=False + Whether to gather tensors with gradients. + cache_labels : bool, default=False + Whether to cache the labels. + + """ + + def __init__( + self, + l2_normalize: bool = False, + local_loss: bool = False, + gather_with_grad: bool = False, + cache_labels: bool = False, + ): + """Initialize the loss.""" + super().__init__() + self.local_loss = local_loss + self.gather_with_grad = gather_with_grad + self.cache_labels = cache_labels + self.l2_normalize = l2_normalize + + # cache state + self._prev_num_logits = 0 + self._labels: Dict[torch.device, torch.Tensor] = {} + + def _get_ground_truth( + self, device: torch.device, num_logits: int, rank: int, world_size: int + ) -> torch.Tensor: + """Return the ground-truth labels. + + Parameters + ---------- + device : torch.device + Device to store the labels. + num_logits : int + Number of logits. + rank : int + Rank of the current process. + world_size : int + Number of processes. + + Returns + ------- + torch.Tensor + Ground-truth labels. + """ + # calculate ground-truth and cache if enabled + if self._prev_num_logits != num_logits or device not in self._labels: + labels = torch.arange(num_logits, device=device, dtype=torch.long) + if world_size > 1 and self.local_loss: + labels = labels + num_logits * rank + if self.cache_labels: + self._labels[device] = labels + self._prev_num_logits = num_logits + else: + labels = self._labels[device] + return labels + + def _get_logits( + self, + features_1: torch.Tensor, + features_2: torch.Tensor, + rank: int, + world_size: int, + ) -> Tuple[torch.Tensor, torch.Tensor]: + """Return the logits. + + Parameters + ---------- + features_1 : torch.Tensor + First feature tensor. + features_2 : torch.Tensor + Second feature tensor + rank : int + Rank of the current process. + world_size : int + Number of processes. + + Returns + ------- + Tuple[torch.Tensor, torch.Tensor] + Logits per feature_1 and feature_2, respectively. + + """ + if world_size > 1: + all_features_1 = gather_features( + features_1, self.local_loss, self.gather_with_grad, rank + ) + all_features_2 = gather_features( + features_2, self.local_loss, self.gather_with_grad, rank + ) + + if self.local_loss: + logits_per_feature_1 = _safe_matmul(features_1, all_features_2) + logits_per_feature_2 = _safe_matmul(features_2, all_features_1) + else: + logits_per_feature_1 = _safe_matmul(all_features_1, all_features_2) + logits_per_feature_2 = logits_per_feature_1.T + else: + logits_per_feature_1 = _safe_matmul(features_1, features_2) + logits_per_feature_2 = _safe_matmul(features_2, features_1) + + return logits_per_feature_1, logits_per_feature_2 + + def forward( + self, features_1: torch.Tensor, features_2: torch.Tensor + ) -> torch.Tensor: + """Calculate the CLIP-style loss between two sets of features. + + Parameters + ---------- + features_1 : torch.Tensor + First set of features. + features_2 : torch.Tensor + Second set of features. + + Returns + ------- + torch.Tensor + Loss value. + """ + world_size = dist.get_world_size() if dist.is_initialized() else 1 + rank = dist.get_rank() if world_size > 1 else 0 + + if self.l2_normalize: + features_1 = F.normalize(features_1, p=2, dim=-1) + features_2 = F.normalize(features_2, p=2, dim=-1) + + logits_per_feat1, logits_per_feat2 = self._get_logits( + features_1, features_2, rank=rank, world_size=world_size + ) + labels = self._get_ground_truth( + features_1.device, + logits_per_feat1.shape[0], + rank=rank, + world_size=world_size, + ) + + return ( + F.cross_entropy(logits_per_feat1, labels) + + F.cross_entropy(logits_per_feat2, labels) + ) / 2 + + +def gather_features( + features: torch.Tensor, + local_loss: bool = False, + gather_with_grad: bool = False, + rank: int = 0, +) -> torch.Tensor: + """Gather features across all processes. + + Parameters + ---------- + features : torch.Tensor + First feature tensor to gather. + local_loss : bool, default=False + Whether to calculate the loss locally i.e. + `matmul(local_features, global_features)`. If False, this method ensures + that the gathered features contain local features for the current rank. + gather_with_grad : bool, default=False + Whether to gather tensors with gradients. + rank : int, default=0 + Rank of the current process. + + Returns + ------- + torch.Tensor + Gathered features. + """ + if gather_with_grad: + all_features = torch.cat(torch.distributed.nn.all_gather(features), dim=0) + else: + gathered_features = gather_all_tensors(features) + if not local_loss: + # ensure grads for local rank when all_* features don't have a gradient + gathered_features[rank] = features + all_features = torch.cat(gathered_features, dim=0) + + return all_features diff --git a/mmlearn/modules/lr_schedulers/__init__.py b/mmlearn/modules/lr_schedulers/__init__.py new file mode 100644 index 0000000..ec326f6 --- /dev/null +++ b/mmlearn/modules/lr_schedulers/__init__.py @@ -0,0 +1,8 @@ +"""Learning rate schedulers for training models.""" + +from mmlearn.modules.lr_schedulers.linear_warmup_cosine_lr import ( + linear_warmup_cosine_annealing_lr, +) + + +__all__ = ["linear_warmup_cosine_annealing_lr"] diff --git a/mmlearn/modules/lr_schedulers/linear_warmup_cosine_lr.py b/mmlearn/modules/lr_schedulers/linear_warmup_cosine_lr.py new file mode 100644 index 0000000..8fe4217 --- /dev/null +++ b/mmlearn/modules/lr_schedulers/linear_warmup_cosine_lr.py @@ -0,0 +1,85 @@ +"""Linear warmup cosine annealing learning rate scheduler.""" + +from hydra_zen import MISSING, store +from torch.optim import Optimizer +from torch.optim.lr_scheduler import ( + CosineAnnealingLR, + LinearLR, + LRScheduler, + SequentialLR, +) + + +@store( # type: ignore[misc] + group="modules/lr_schedulers", + provider="mmlearn", + zen_partial=True, + warmup_steps=MISSING, + max_steps=MISSING, +) +def linear_warmup_cosine_annealing_lr( + optimizer: Optimizer, + warmup_steps: int, + max_steps: int, + start_factor: float = 1 / 3, + eta_min: float = 0.0, + last_epoch: int = -1, +) -> LRScheduler: + """Create a linear warmup cosine annealing learning rate scheduler. + + Parameters + ---------- + optimizer : Optimizer + Wrapped optimizer. + warmup_steps : int + Maximum number of iterations for linear warmup. + max_steps : int + Maximum number of iterations. + start_factor : float, default=1/3 + Multiplicative factor for the learning rate at the start of the warmup phase. + eta_min : float, default=0 + Minimum learning rate. + last_epoch : int, default=-1 + The index of last epoch. If set to -1, it initializes the learning rate + as the base learning rate + + Returns + ------- + LRScheduler + The learning rate scheduler. + + Raises + ------ + ValueError + If `warmup_steps` is greater than or equal to `max_steps` or if `warmup_steps` + is less than or equal to 0. + """ + if warmup_steps >= max_steps: + raise ValueError( + "Expected `warmup_steps` to be less than `max_steps` but got " + f"`warmup_steps={warmup_steps}` and `max_steps={max_steps}`." + ) + if warmup_steps <= 0: + raise ValueError( + "Expected `warmup_steps` to be positive but got " + f"`warmup_steps={warmup_steps}`." + ) + + linear_lr = LinearLR( + optimizer, + start_factor=start_factor, + total_iters=warmup_steps, + last_epoch=last_epoch, + ) + cosine_lr = CosineAnnealingLR( + optimizer, + T_max=max_steps - warmup_steps - 1, + eta_min=eta_min, + last_epoch=last_epoch, + ) + return SequentialLR( + optimizer, + schedulers=[linear_lr, cosine_lr], + milestones=[warmup_steps], + last_epoch=last_epoch, + ) diff --git a/mmlearn/modules/metrics/__init__.py b/mmlearn/modules/metrics/__init__.py new file mode 100644 index 0000000..ee54c61 --- /dev/null +++ b/mmlearn/modules/metrics/__init__.py @@ -0,0 +1,6 @@ +"""Metrics for evaluating models.""" + +from mmlearn.modules.metrics.retrieval_recall import RetrievalRecallAtK + + +__all__ = ["RetrievalRecallAtK"] diff --git a/mmlearn/modules/metrics/retrieval_recall.py b/mmlearn/modules/metrics/retrieval_recall.py new file mode 100644 index 0000000..abee79d --- /dev/null +++ b/mmlearn/modules/metrics/retrieval_recall.py @@ -0,0 +1,286 @@ +"""Retrieval Recall@K metric.""" + +from functools import partial +from typing import Any, Callable, Dict, List, Literal, Optional, Tuple, Union + +import torch +import torch.distributed +from hydra_zen import store +from torchmetrics import Metric +from torchmetrics.retrieval.base import _retrieval_aggregate +from torchmetrics.utilities.checks import _check_same_shape +from torchmetrics.utilities.compute import _safe_matmul +from torchmetrics.utilities.data import dim_zero_cat +from torchmetrics.utilities.distributed import gather_all_tensors + + +@store(group="modules/metrics", provider="mmlearn") +class RetrievalRecallAtK(Metric): + """Retrieval Recall@K metric. + + Computes the Recall@K for retrieval tasks. The metric is computed as follows: + + 1. Compute the cosine similarity between the query and the database. + 2. For each query, sort the database in decreasing order of similarity. + 3. Compute the Recall@K as the number of true positives among the top K elements. + + Parameters + ---------- + top_k : int + The number of top elements to consider for computing the Recall@K. + reduction : {"mean", "sum", "none", None}, default="sum" + Specifies the reduction to apply after computing the pairwise cosine similarity + scores. + aggregation : {"mean", "median", "min", "max"} or callable, default="mean" + Specifies the aggregation function to apply to the Recall@K values computed + in batches. If a callable is provided, it should accept a tensor of values + and a keyword argument 'dim' and return a single scalar value. + kwargs : Any + Additional arguments to be passed to the torchmetrics.Metric class. + + """ + + is_differentiable: bool = False + higher_is_better: bool = True + full_state_update: bool = False + + indexes: List[torch.Tensor] + x: List[torch.Tensor] + y: List[torch.Tensor] + num_samples: torch.Tensor + + def __init__( + self, + top_k: int, + reduction: Literal["mean", "sum", "none", None] = "sum", + aggregation: Union[ + Literal["mean", "median", "min", "max"], + Callable[[torch.Tensor, int], torch.Tensor], + ] = "mean", + **kwargs: Any, + ) -> None: + """Initialize the metric.""" + super().__init__(**kwargs) + + if top_k is not None and not (isinstance(top_k, int) and top_k > 0): + raise ValueError("`top_k` has to be a positive integer or None") + self.top_k = top_k + + allowed_reduction = ("sum", "mean", "none", None) + if reduction not in allowed_reduction: + raise ValueError( + f"Expected argument `reduction` to be one of {allowed_reduction} but got {reduction}" + ) + self.reduction = reduction + + if not ( + aggregation in ("mean", "median", "min", "max") or callable(aggregation) + ): + raise ValueError( + "Argument `aggregation` must be one of `mean`, `median`, `min`, `max` or a custom callable function" + f"which takes tensor of values, but got {aggregation}." + ) + self.aggregation = aggregation + + self.add_state("x", default=[], dist_reduce_fx="cat") + self.add_state("y", default=[], dist_reduce_fx="cat") + self.add_state("indexes", default=[], dist_reduce_fx="cat") + self.add_state("num_samples", default=torch.tensor(0), dist_reduce_fx="cat") + + self._batch_size: int = -1 + + self.compute_on_cpu = True + self.sync_on_compute = False + self.dist_sync_on_step = False + self._to_sync = self.sync_on_compute + self._should_unsync = False + + def _is_distributed(self) -> bool: + if self.distributed_available_fn is not None: + distributed_available = self.distributed_available_fn + + return distributed_available() if callable(distributed_available) else False # type: ignore[no-any-return] + + def update(self, x: torch.Tensor, y: torch.Tensor, indexes: torch.Tensor) -> None: + """Check shape, convert dtypes and add to accumulators. + + Parameters + ---------- + x : torch.Tensor + Embeddings (unnormalized) of shape `(N, D)` where `N` is the number + of samples and `D` is the number of dimensions. + y : torch.Tensor + Embeddings (unnormalized) of shape `(M, D)` where `M` is the number + of samples and `D` is the number of dimensions. + indexes : torch.Tensor + Index tensor of shape `(N,)` where `N` is the number of samples. + This specifies which sample in 'y' is the positive match for each + sample in 'x'. + + """ + if indexes is None: + raise ValueError("Argument `indexes` cannot be None") + + x, y, indexes = _update_batch_inputs(x.clone(), y.clone(), indexes.clone()) + + # offset batch indexes by the number of samples seen so far + indexes += self.num_samples + + local_batch_size = torch.zeros_like(self.num_samples) + x.size(0) + if self._is_distributed(): + x = dim_zero_cat(gather_all_tensors(x, self.process_group)) + y = dim_zero_cat(gather_all_tensors(y, self.process_group)) + indexes = dim_zero_cat( + gather_all_tensors(indexes.clone(), self.process_group) + ) + + # offset indexes for each device + bsz_per_device = dim_zero_cat( + gather_all_tensors(local_batch_size, self.process_group) + ) + cum_local_bsz = torch.cumsum(bsz_per_device, dim=0) + for device_idx in range(1, bsz_per_device.numel()): + indexes[cum_local_bsz[device_idx - 1] : cum_local_bsz[device_idx]] += ( + cum_local_bsz[device_idx - 1] + ) + + # update the global sample count + self.num_samples += cum_local_bsz[-1] + + self._is_synced = True + else: + self.num_samples += x.size(0) + + self.x.append(x) + self.y.append(y) + self.indexes.append(indexes) + + if self._batch_size == -1: + self._batch_size = x.size(0) # global batch size + + def compute(self) -> torch.Tensor: + """Compute the metric. + + Returns + ------- + torch.Tensor + The computed metric. + """ + x = dim_zero_cat(self.x) + y = dim_zero_cat(self.y) + + # compute the cosine similarity + x_norm = x / x.norm(dim=-1, p=2, keepdim=True) + y_norm = y / y.norm(dim=-1, p=2, keepdim=True) + similarity = _safe_matmul(x_norm, y_norm) + reduction_mapping: Dict[ + Optional[str], Callable[[torch.Tensor], torch.Tensor] + ] = { + "sum": partial(torch.sum, dim=-1), + "mean": partial(torch.mean, dim=-1), + "none": lambda x: x, + None: lambda x: x, + } + scores: torch.Tensor = reduction_mapping[self.reduction](similarity) + + indexes = dim_zero_cat(self.indexes) + positive_pairs = torch.zeros_like(scores, dtype=torch.bool) + positive_pairs[torch.arange(len(scores)), indexes] = True + + results = [] + for start in range(0, len(scores), self._batch_size): + end = start + self._batch_size + x = scores[start:end] + y = positive_pairs[start:end] + result = recall_at_k(x, y, self.top_k) + results.append(result) + + return _retrieval_aggregate( + (torch.cat([x.to(scores) for x in results]) > 0).float(), self.aggregation + ) + + def forward(self, *args: Any, **kwargs: Any) -> Any: + """Forward method is not supported.""" + raise NotImplementedError( + "RetrievalRecallAtK metric does not support forward method" + ) + + +# modified from: +# https://github.com/LAION-AI/CLIP_benchmark/blob/main/clip_benchmark/metrics/zeroshot_retrieval.py +def recall_at_k( + scores: torch.Tensor, positive_pairs: torch.Tensor, k: int +) -> torch.Tensor: + """Compute the recall at k for each sample. + + Parameters + ---------- + scores : torch.Tensor + Compatibility score between embeddings (num_x, num_y). + positive_pairs : torch.Tensor + Boolean matrix of positive pairs (num_x, num_y). + k : int + Consider only the top k elements for each query. + + Returns + ------- + recall at k averaged over all texts + """ + nb_texts, nb_images = scores.shape + # for each text, sort according to image scores in decreasing order + topk_indices = torch.topk(scores, k, dim=1)[1] + # compute number of positives for each text + nb_positive = positive_pairs.sum(dim=1) + # nb_texts, k, nb_images + topk_indices_onehot = torch.nn.functional.one_hot( + topk_indices, num_classes=nb_images + ) + # compute number of true positives + positive_pairs_reshaped = positive_pairs.view(nb_texts, 1, nb_images) + # a true positive means a positive among the topk + nb_true_positive = (topk_indices_onehot * positive_pairs_reshaped).sum(dim=(1, 2)) + # compute recall at k + return nb_true_positive / nb_positive + + +def _update_batch_inputs( + x: torch.Tensor, + y: torch.Tensor, + indexes: torch.Tensor, +) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]: + """Update and returns variables required to compute Retrieval Recall. + + Checks for same shape of input tensors. + + Parameters + ---------- + x : torch.Tensor + Predicted tensor. + y : torch.Tensor + Ground truth tensor. + indexes : torch.Tensor + Index tensor. + + Returns + ------- + Tuple[torch.Tensor, torch.Tensor, torch.Tensor] + Returns updated tensors required to compute Retrieval Recall. + + """ + _check_same_shape(x, y) + if x.ndim != 2: + raise ValueError( + "Expected input to retrieval recall to be 2D tensors of shape `[N,D]`, " + "where `N` is the number of samples and `D` is the number of dimensions, " + f"but got tensor of shape {x.shape}" + ) + if not indexes.numel() or not indexes.size(): + raise ValueError( + "`indexes`, `x` and `y` must be non-empty and non-scalar tensors", + ) + + x = x.float() + y = y.float() + indexes = indexes.long() + + return x, y, indexes diff --git a/mmlearn/tasks/__init__.py b/mmlearn/tasks/__init__.py new file mode 100644 index 0000000..6816d5e --- /dev/null +++ b/mmlearn/tasks/__init__.py @@ -0,0 +1,10 @@ +"""Modules for pretraining, downstream and evaluation tasks.""" + +from mmlearn.tasks.contrastive_pretraining import ContrastivePretraining +from mmlearn.tasks.zero_shot_retrieval import ZeroShotCrossModalRetrieval + + +__all__ = [ + "ContrastivePretraining", + "ZeroShotCrossModalRetrieval", +] diff --git a/mmlearn/tasks/contrastive_pretraining.py b/mmlearn/tasks/contrastive_pretraining.py new file mode 100644 index 0000000..ad229df --- /dev/null +++ b/mmlearn/tasks/contrastive_pretraining.py @@ -0,0 +1,595 @@ +"""Contrastive pretraining task.""" + +import itertools +from dataclasses import dataclass +from typing import Any, Callable, Dict, List, Literal, Mapping, Optional, Tuple, Union + +import lightning as L # noqa: N812 +import torch +import torch.distributed +import torch.distributed.nn +from hydra_zen import store +from lightning.pytorch.utilities.types import OptimizerLRScheduler +from lightning_utilities.core.rank_zero import rank_zero_warn +from torch import nn + +from mmlearn.datasets.core import Modalities, find_matching_indices +from mmlearn.datasets.core.modalities import Modality +from mmlearn.modules.losses import CLIPLoss +from mmlearn.tasks.hooks import EvaluationHooks + + +_unsupported_modality_error = ( + "Found unsupported modality `{}` in the input. Supported modalities are " + f"{Modalities.list_modalities()}." + "HINT: New modalities can be added with `Modalities.register_modality` method." +) + + +@dataclass +class ModuleKeySpec: + """Module key specification for mapping modules to modalities.""" + + encoder_key: Optional[str] = None + head_key: Optional[str] = None + postprocessor_key: Optional[str] = None + + +@dataclass +class LossPairSpec: + """Specification for a pair of modalities to compute the contrastive loss.""" + + modalities: Tuple[str, str] + weight: float = 1.0 + + +@dataclass +class AuxiliaryTaskSpec: + """Specification for an auxiliary task to run alongside the main task.""" + + modality: str + task: Callable[[nn.Module], L.LightningModule] + loss_weight: float = 1.0 + + +@dataclass +class EvaluationSpec: + """Specification for an evaluation task.""" + + task: ( + Any # NOTE: hydra/omegaconf does not support custom types in structured configs + ) + run_on_validation: bool = True + run_on_test: bool = True + + +@store(group="task", provider="mmlearn") +class ContrastivePretraining(L.LightningModule): + """Contrastive pretraining task. + + This class supports contrastive pretraining with `N` modalities of data. It + allows the sharing of encoders, heads, and postprocessors across modalities. + It also supports computing the contrastive loss between specified pairs of + modalities, as well as training auxiliary tasks alongside the main contrastive + pretraining task. + + Parameters + ---------- + encoders : Dict[str, nn.Module] + A dictionary of encoders. The keys can be any string, including the names of + any supported modalities. If the keys are not supported modalities, the + `modality_module_mapping` parameter must be provided to map the encoders to + specific modalities. The encoders are expected to take a dictionary of input + values and return a list-like object with the first element being the encoded + values. This first element is passed on to the heads or postprocessors and + the remaining elements are ignored. + heads : Dict[str, Union[nn.Module, Dict[str, nn.Module]]], optional, default=None + A dictionary of modules that process the encoder outputs, usually projection + heads. If the keys do not correspond to the name of a supported modality, + the `modality_module_mapping` parameter must be provided. If any of the values + are dictionaries, they will be wrapped in a `nn.Sequential` module. All + head modules are expected to take a single input tensor and return a single + output tensor. + postprocessors : Dict[str, Union[nn.Module, Dict[str, nn.Module]]], optional, default=None + A dictionary of modules that process the head outputs. If the keys do not + correspond to the name of a supported modality, the `modality_module_mapping` + parameter must be provided. If any of the values are dictionaries, they will + be wrapped in a `nn.Sequential` module. All postprocessor modules are expected + to take a single input tensor and return a single output tensor. + modality_module_mapping : Dict[str, ModuleKeySpec], optional, default=None + A dictionary mapping modalities to encoders, heads, and postprocessors. + Useful for reusing the same instance of a module across multiple modalities. + optimizer : torch.optim.Optimizer, optional, default=None + The optimizer to use. + lr_scheduler : Union[Dict[str, torch.optim.lr_scheduler.LRScheduler], torch.optim.lr_scheduler.LRScheduler], optional, default=None + The learning rate scheduler to use. + loss : CLIPLoss, optional, default=None + The loss function to use. + modality_loss_pairs : List[LossPairSpec], optional, default=None + A list of pairs of modalities to compute the contrastive loss between and + the weight to apply to each pair. + auxiliary_tasks : Dict[str, AuxiliaryTaskSpec], optional, default=None + Auxiliary tasks to run alongside the main contrastive pretraining task. + The auxiliary task module is expected to be a partially-initialized + instance of a `LightningModule`, such that an initialized encoder can be + passed as the first argument to the module. The `modality` parameter + specifies the modality of the encoder to use for the auxiliary task. + log_auxiliary_tasks_loss : bool, optional, default=False + Whether to log the loss of auxiliary tasks to the main logger. + compute_validation_loss : bool, optional, default=True + Whether to compute the validation loss if a validation dataloader is provided. + The loss function must be provided to compute the validation loss. + compute_test_loss : bool, optional, default=True + Whether to compute the test loss if a test dataloader is provided. The loss + function must be provided to compute the test loss. + evaluation_tasks : Dict[str, EvaluationSpec], optional, default=None + Evaluation tasks to run during validation, while training, and during testing. + + """ # noqa: W505 + + def __init__( # noqa: PLR0912, PLR0915 + self, + encoders: Dict[str, nn.Module], + heads: Optional[Dict[str, Union[nn.Module, Dict[str, nn.Module]]]] = None, + postprocessors: Optional[ + Dict[str, Union[nn.Module, Dict[str, nn.Module]]] + ] = None, + modality_module_mapping: Optional[Dict[str, ModuleKeySpec]] = None, + optimizer: Optional[torch.optim.Optimizer] = None, + lr_scheduler: Optional[ + Union[ + Dict[str, torch.optim.lr_scheduler.LRScheduler], + torch.optim.lr_scheduler.LRScheduler, + ] + ] = None, + loss: Optional[CLIPLoss] = None, + modality_loss_pairs: Optional[List[LossPairSpec]] = None, + auxiliary_tasks: Optional[Dict[str, AuxiliaryTaskSpec]] = None, + log_auxiliary_tasks_loss: bool = False, + compute_validation_loss: bool = True, + compute_test_loss: bool = True, + evaluation_tasks: Optional[Dict[str, EvaluationSpec]] = None, + ) -> None: + """Initialize the module.""" + super().__init__() + + if modality_module_mapping is None: + # assume all the module dictionaries use the same keys corresponding + # to modalities + modality_module_mapping = {} + for key in encoders: + modality_module_mapping[key] = ModuleKeySpec( + encoder_key=key, + head_key=key, + postprocessor_key=key, + ) + + # match modalities to encoders, heads, and postprocessors + modality_encoder_mapping: Dict[str, Optional[str]] = {} + modality_head_mapping: Dict[str, Optional[str]] = {} + modality_postprocessor_mapping: Dict[str, Optional[str]] = {} + for modality_key, module_mapping in modality_module_mapping.items(): + if not Modalities.has_modality(modality_key): + raise ValueError(_unsupported_modality_error.format(modality_key)) + modality_encoder_mapping[modality_key] = module_mapping.encoder_key + modality_head_mapping[modality_key] = module_mapping.head_key + modality_postprocessor_mapping[modality_key] = ( + module_mapping.postprocessor_key + ) + + # ensure all modules are mapped to a modality + for key in encoders: + if key not in modality_encoder_mapping.values(): + if not Modalities.has_modality(key): + raise ValueError(_unsupported_modality_error.format(key)) + modality_encoder_mapping[key] = key + + if heads is not None: + for key in heads: + if key not in modality_head_mapping.values(): + if not Modalities.has_modality(key): + raise ValueError(_unsupported_modality_error.format(key)) + modality_head_mapping[key] = key + + if postprocessors is not None: + for key in postprocessors: + if key not in modality_postprocessor_mapping.values(): + if not Modalities.has_modality(key): + raise ValueError(_unsupported_modality_error.format(key)) + modality_postprocessor_mapping[key] = key + + self._available_modalities: List[Modality] = [ + Modalities.get_modality(modality_key) + for modality_key in modality_encoder_mapping + ] + assert ( + len(self._available_modalities) >= 2 + ), "Expected at least two modalities to be available. " + + self.encoders = nn.ModuleDict( + { + Modalities.get_modality(modality_key): encoders[encoder_key] + for modality_key, encoder_key in modality_encoder_mapping.items() + if encoder_key is not None + } + ) + self.heads = None + if heads is not None: + self.heads = nn.ModuleDict( + { + Modalities.get_modality(modality_key): heads[head_key] + if isinstance(heads[head_key], nn.Module) + else nn.Sequential(*heads[head_key].values()) + for modality_key, head_key in modality_head_mapping.items() + if head_key is not None + } + ) + + self.postprocessors = None + if postprocessors is not None: + self.postprocessors = nn.ModuleDict( + { + Modalities.get_modality(modality_key): postprocessors[ + postprocessor_key + ] + if isinstance(postprocessors[postprocessor_key], nn.Module) + else nn.Sequential(*postprocessors[postprocessor_key].values()) + for modality_key, postprocessor_key in modality_postprocessor_mapping.items() + if postprocessor_key is not None + } + ) + + if modality_loss_pairs is None: + modality_loss_pairs = [ + LossPairSpec(modalities=(m1.value, m2.value)) + for m1, m2 in itertools.combinations(self._available_modalities, 2) + ] + + for modality_pair in modality_loss_pairs: + if not all( + Modalities.get_modality(modality) in self._available_modalities + for modality in modality_pair.modalities + ): + raise ValueError( + "Found unspecified modality in the loss pair specification " + f"{modality_pair.modalities}. Available modalities are " + f"{self._available_modalities}." + ) + self.modality_loss_pairs = modality_loss_pairs + + self.aux_task_specs = auxiliary_tasks or {} + self.auxiliary_tasks: Dict[str, L.LightningModule] = {} + for task_name, task_spec in self.aux_task_specs.items(): + try: + aux_task_modality = Modalities.get_modality(task_spec.modality) + self.auxiliary_tasks[task_name] = task_spec.task( + self.encoders[aux_task_modality] + ) + except KeyError as exc: + raise ValueError( + f"Found unsupported modality `{task_spec.modality}` in the auxiliary tasks. " + f"Available modalities are {self._available_modalities}." + ) from exc + + if loss is None and (compute_validation_loss or compute_test_loss): + raise ValueError( + "Loss function must be provided to compute validation or test loss." + ) + + self.loss_fn = loss + self.optimizer = optimizer + self.lr_scheduler = lr_scheduler + self.log_auxiliary_tasks_loss = log_auxiliary_tasks_loss + self.compute_validation_loss = compute_validation_loss + self.compute_test_loss = compute_test_loss + self.evaluation_tasks = evaluation_tasks + + def encode( + self, inputs: Dict[Union[str, Modality], Any], modality: Modality + ) -> torch.Tensor: + """Encode the input values for the given modality. + + Parameters + ---------- + inputs : Dict[Union[str, Modality], Any] + Input values. + modality : Modality + The modality to encode. + + Returns + ------- + torch.Tensor + The encoded values for the specified modality. + """ + output = self.encoders[modality](inputs)[0] + + if self.heads and modality in self.heads: + output = self.heads[modality](output) + + if self.postprocessors and modality in self.postprocessors: + output = self.postprocessors[modality](output) + + return output + + def forward( + self, inputs: Dict[Union[str, Modality], Any] + ) -> Dict[str, torch.Tensor]: + """Run the forward pass. + + Parameters + ---------- + inputs : Dict[Union[str, Modality], Any] + The input tensors to encode. + + Returns + ------- + Dict[str, torch.Tensor] + The encodings for each modality. + """ + outputs = { + modality.embedding: self.encode(inputs, modality) + for modality in self._available_modalities + } + + if not all( + output.size(-1) == list(outputs.values())[0].size(-1) + for output in outputs.values() + ): + raise ValueError("Expected all model outputs to have the same dimension.") + + return outputs + + def _compute_loss( + self, + batch: Dict[Union[str, Modality], Any], + batch_idx: int, + outputs: Dict[str, torch.Tensor], + ) -> Optional[torch.Tensor]: + if self.loss_fn is None: + return None + + contrastive_losses: list[torch.Tensor] = [] + for loss_pair in self.modality_loss_pairs: + modality_a = Modalities.get_modality(loss_pair.modalities[0]) + modality_b = Modalities.get_modality(loss_pair.modalities[1]) + + indices_a, indices_b = find_matching_indices( + batch["example_ids"][modality_a], + batch["example_ids"][modality_b], + ) + if indices_a.numel() == 0 or indices_b.numel() == 0: + continue + + contrastive_losses.append( + self.loss_fn( + outputs[modality_a.embedding][indices_a], + outputs[modality_b.embedding][indices_b], + ) + * loss_pair.weight + ) + + auxiliary_losses: list[torch.Tensor] = [] + if self.auxiliary_tasks: + for task_name, task_spec in self.aux_task_specs.items(): + auxiliary_task_output = self.auxiliary_tasks[task_name].training_step( + batch, batch_idx + ) + if isinstance(auxiliary_task_output, torch.Tensor): + auxiliary_task_loss = auxiliary_task_output + elif isinstance(auxiliary_task_output, Mapping): + auxiliary_task_loss = auxiliary_task_output["loss"] + else: + raise ValueError( + "Expected auxiliary task output to be a tensor or a mapping " + f"containing a 'loss' key, but got {type(auxiliary_task_output)}." + ) + + auxiliary_losses.append(task_spec.loss_weight * auxiliary_task_loss) + if self.log_auxiliary_tasks_loss: + self.log( + f"train/{task_name}_loss", + auxiliary_task_loss + if not self.fabric + else self.all_gather( + auxiliary_task_loss.clone().detach() + ).mean(), + sync_dist=True, + ) + + return torch.stack(contrastive_losses + auxiliary_losses).sum() + + def training_step( + self, batch: Dict[Union[str, Modality], Any], batch_idx: int + ) -> torch.Tensor: + """Compute the loss for the batch. + + Parameters + ---------- + batch : Dict[Union[str, Modality], Any] + The batch of data to process. + batch_idx : int + The index of the batch. + + Returns + ------- + torch.Tensor + The loss for the batch. + """ + outputs = self(batch) + loss = self._compute_loss(batch, batch_idx, outputs) + if loss is None: + raise ValueError("The loss function must be provided for training.") + + self.log( + "train/loss", + loss if not self.fabric else self.all_gather(loss.clone().detach()).mean(), + prog_bar=True, + sync_dist=True, + ) + + return loss + + def on_validation_epoch_start(self) -> None: + """Prepare for the validation epoch.""" + self._on_eval_epoch_start("val") + + def validation_step( + self, batch: Dict[str, torch.Tensor], batch_idx: int + ) -> Optional[torch.Tensor]: + """Run a single validation step. + + Parameters + ---------- + batch : Dict[Union[str, Modality], Any] + The batch of data to process. + batch_idx : int + The index of the batch. + + Returns + ------- + torch.Tensor or None + The loss for the batch or None if the loss function is not provided. + """ + return self._shared_eval_step(batch, batch_idx, "val") + + def on_validation_epoch_end(self) -> None: + """Compute and log epoch-level metrics at the end of the validation epoch.""" + self._on_eval_epoch_end("val") + + def on_test_epoch_start(self) -> None: + """Prepare for the test epoch.""" + self._on_eval_epoch_start("test") + + def test_step( + self, batch: Dict[str, torch.Tensor], batch_idx: int + ) -> Optional[torch.Tensor]: + """Run a single test step. + + Parameters + ---------- + batch : Dict[Union[str, Modality], Any] + The batch of data to process. + batch_idx : int + The index of the batch. + + Returns + ------- + torch.Tensor or None + The loss for the batch or None if the loss function is not provided. + """ + return self._shared_eval_step(batch, batch_idx, "test") + + def on_test_epoch_end(self) -> None: + """Compute and log epoch-level metrics at the end of the test epoch.""" + self._on_eval_epoch_end("test") + + def configure_optimizers(self) -> OptimizerLRScheduler: + """Configure the optimizer and learning rate scheduler.""" + if self.optimizer is None: + rank_zero_warn( + "Optimizer not provided. Training will continue without an optimizer. " + "LR scheduler will not be used.", + ) + return None + + optimizer: torch.optim.Optimizer = self.optimizer(params=self.parameters()) + + if self.lr_scheduler is not None: + if isinstance(self.lr_scheduler, torch.optim.lr_scheduler.LRScheduler): + return [optimizer], [self.lr_scheduler(optimizer)] + + if isinstance(self.lr_scheduler, dict) and "scheduler" in self.lr_scheduler: + lr_scheduler = self.lr_scheduler["scheduler"](optimizer=optimizer) + if not isinstance(lr_scheduler, torch.optim.lr_scheduler.LRScheduler): + raise TypeError( + "Expected scheduler to be an instance of " + f"`torch.optim.lr_scheduler.LRScheduler`, but got {type(lr_scheduler)}.", + ) + lr_scheduler_dict = {"scheduler": lr_scheduler} + + if self.lr_scheduler.get("extras"): + lr_scheduler_dict.update(self.lr_scheduler["extras"]) + return {"optimizer": optimizer, "lr_scheduler": lr_scheduler_dict} + + return optimizer + + def _on_eval_epoch_start(self, eval_type: Literal["val", "test"]) -> None: + """Prepare for the evaluation epoch.""" + if self.evaluation_tasks: + for task_spec in self.evaluation_tasks.values(): + if (eval_type == "val" and task_spec.run_on_validation) or ( + eval_type == "test" and task_spec.run_on_test + ): + if not isinstance(task_spec.task, EvaluationHooks): + raise TypeError( + f"Expected {task_spec.task} to be an instance of " + f"`EvaluationHooks` but got {task_spec.task.__bases__}." + ) + task_spec.task.on_evaluation_epoch_start(self) + + def _shared_eval_step( + self, + batch: Dict[str, torch.Tensor], + batch_idx: int, + eval_type: Literal["val", "test"], + ) -> Optional[torch.Tensor]: + """Run a single evaluation step. + + Parameters + ---------- + batch : Dict[Union[str, Modality], Any] + The batch of data to process. + batch_idx : int + The index of the batch. + + Returns + ------- + torch.Tensor or None + The loss for the batch or None if the loss function is not provided. + """ + outputs = self(batch) + loss: Optional[torch.Tensor] = None + if (eval_type == "val" and self.compute_validation_loss) or ( + eval_type == "test" and self.compute_test_loss + ): + loss = self._compute_loss(batch, batch_idx, outputs) + if loss is not None: + self.log( + f"{eval_type}/loss", + loss + if not self.fabric + else self.all_gather(loss.clone().detach()).mean(), + prog_bar=True, + sync_dist=True, + ) + + if self.evaluation_tasks: + for task_spec in self.evaluation_tasks.values(): + if (eval_type == "val" and task_spec.run_on_validation) or ( + eval_type == "test" and task_spec.run_on_test + ): + batch_result = task_spec.task.evaluation_step( + self.trainer, self, batch, batch_idx + ) + if batch_result: + for key, value in batch_result.items(): + self.log( + f"{eval_type}/{key}_step", + value, + on_step=True, + on_epoch=False, + sync_dist=True, + ) + return loss + + def _on_eval_epoch_end(self, eval_type: Literal["val", "test"]) -> None: + """Compute and log epoch-level metrics at the end of the evaluation epoch.""" + if self.evaluation_tasks: + for task_spec in self.evaluation_tasks.values(): + if (eval_type == "val" and task_spec.run_on_validation) or ( + eval_type == "test" and task_spec.run_on_test + ): + results = task_spec.task.on_evaluation_epoch_end(self) + if results: + for key, value in results.items(): + self.log(f"{eval_type}/{key}", value) diff --git a/mmlearn/tasks/hooks.py b/mmlearn/tasks/hooks.py new file mode 100644 index 0000000..7ca15fc --- /dev/null +++ b/mmlearn/tasks/hooks.py @@ -0,0 +1,65 @@ +"""Task-related hooks for Lightning modules.""" + +from typing import Any, Mapping, Optional, Union + +import lightning.pytorch as pl +from lightning.pytorch.utilities.rank_zero import rank_zero_warn + + +class EvaluationHooks: + """Hooks for evaluation.""" + + def on_evaluation_epoch_start(self, pl_module: pl.LightningModule) -> None: + """Prepare the evaluation loop. + + Parameters + ---------- + pl_module : pl.LightningModule + A reference to the Lightning module being evaluated. + """ + + def evaluation_step( + self, + trainer: pl.Trainer, + pl_module: pl.LightningModule, + batch: Any, + batch_idx: int, + ) -> Optional[Mapping[str, Any]]: + """Run a single evaluation step. + + Parameters + ---------- + pl_module : pl.LightningModule + A reference to the Lightning module being evaluated. + batch : Any + A batch of data. + batch_idx : int + The index of the batch. + + Returns + ------- + Mapping[str, Any] or None + A dictionary of evaluation results for the batch or `None` if no + batch results are available. + + """ + rank_zero_warn( + f"`evaluation_step` must be implemented to use {self.__class__.__name__} for evaluation." + ) + return None + + def on_evaluation_epoch_end( + self, pl_module: pl.LightningModule + ) -> Optional[Union[Mapping[str, Any]]]: + """Run after the evaluation epoch. + + Parameters + ---------- + pl_module : pl.LightningModule + A reference to the Lightning module being evaluated. + + Returns + ------- + Optional[Union[Mapping[str, Any]]] + A dictionary of evaluation results or `None` if no results are available. + """ diff --git a/mmlearn/tasks/zero_shot_retrieval.py b/mmlearn/tasks/zero_shot_retrieval.py new file mode 100644 index 0000000..982dbca --- /dev/null +++ b/mmlearn/tasks/zero_shot_retrieval.py @@ -0,0 +1,119 @@ +"""Zero-shot cross-modal retrieval evaluation task.""" + +from dataclasses import dataclass +from typing import Any, Dict, List, Tuple, Union + +import lightning.pytorch as pl +import torch +import torch.distributed +import torch.distributed.nn +from hydra_zen import store +from torchmetrics import MetricCollection + +from mmlearn.datasets.core import Modalities +from mmlearn.datasets.core.modalities import Modality +from mmlearn.modules.metrics import RetrievalRecallAtK +from mmlearn.tasks.hooks import EvaluationHooks + + +@dataclass +class RetrievalTaskSpec: + """Specification for a retrieval task.""" + + query_modality: str + target_modality: str + top_k: List[int] + + +@store(group="eval_task", provider="mmlearn") +class ZeroShotCrossModalRetrieval(EvaluationHooks): + """Zero-shot cross-modal retrieval evaluation task. + + This task evaluates the retrieval performance of a model on a set of query-target + pairs. The model is expected to produce embeddings for both the query and target + modalities. The task computes the retrieval recall at `k` for each pair of + modalities. + + Parameters + ---------- + task_specs : List[RetrievalTaskSpec] + A list of retrieval task specifications. Each specification defines the query + and target modalities, as well as the top-k values for which to compute the + retrieval recall metrics. + + """ + + def __init__(self, task_specs: List[RetrievalTaskSpec]): + """Initialize the module.""" + super().__init__() + + self.task_specs = task_specs + self.metrics: Dict[Tuple[Modality, Modality], MetricCollection] = {} + + for spec in self.task_specs: + assert Modalities.has_modality(spec.query_modality) + assert Modalities.has_modality(spec.target_modality) + + query_modality = Modalities.get_modality(spec.query_modality) + target_modality = Modalities.get_modality(spec.target_modality) + + self.metrics[(query_modality, target_modality)] = MetricCollection( + { + f"{query_modality}_to_{target_modality}_R@{k}": RetrievalRecallAtK( + top_k=k, aggregation="mean", reduction="none" + ) + for k in spec.top_k + } + ) + + def on_evaluation_epoch_start(self, pl_module: pl.LightningModule) -> None: + """Move the metrics to the device of the Lightning module.""" + for metric in self.metrics.values(): + metric.to(pl_module.device) + + def evaluation_step( + self, + trainer: pl.Trainer, + pl_module: pl.LightningModule, + batch: Dict[str, torch.Tensor], + batch_idx: int, + ) -> None: + """Run the forward pass and update retrieval recall metrics. + + Parameters + ---------- + trainer : pl.Trainer + A reference to the Lightning Trainer. + pl_module : pl.LightningModule + A reference to the Lightning module being evaluated. + batch : Dict[str, torch.Tensor] + A dictionary of batched input tensors. + batch_idx : int + The index of the batch. + + """ + if trainer.sanity_checking: + return + + outputs: Dict[Union[str, Modality], Any] = pl_module(batch) + for (query_modality, target_modality), metric in self.metrics.items(): + query_embeddings: torch.Tensor = outputs[query_modality.embedding] + target_embeddings: torch.Tensor = outputs[target_modality.embedding] + indexes = torch.arange(query_embeddings.size(0), device=pl_module.device) + + metric.update(query_embeddings, target_embeddings, indexes) + + def on_evaluation_epoch_end(self, pl_module: pl.LightningModule) -> Dict[str, Any]: + """Compute the retrieval recall metrics. + + Parameters + ---------- + pl_module : pl.LightningModule + A reference to the Lightning module being evaluated. + """ + results = {} + for metric in self.metrics.values(): + results.update(metric.compute()) + metric.reset() + + return results diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000..8c832f1 --- /dev/null +++ b/poetry.lock @@ -0,0 +1,4683 @@ +# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. + +[[package]] +name = "accelerate" +version = "0.31.0" +description = "Accelerate" +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "accelerate-0.31.0-py3-none-any.whl", hash = "sha256:0fc608dc49584f64d04711a39711d73cb0ad4ef3d21cddee7ef2216e29471144"}, + {file = "accelerate-0.31.0.tar.gz", hash = "sha256:b5199865b26106ccf9205acacbe8e4b3b428ad585e7c472d6a46f6fb75b6c176"}, +] + +[package.dependencies] +huggingface-hub = "*" +numpy = ">=1.17" +packaging = ">=20.0" +psutil = "*" +pyyaml = "*" +safetensors = ">=0.3.1" +torch = ">=1.10.0" + +[package.extras] +deepspeed = ["deepspeed (<=0.14.0)"] +dev = ["bitsandbytes", "black (>=23.1,<24.0)", "datasets", "diffusers", "evaluate", "hf-doc-builder (>=0.3.0)", "parameterized", "pytest (>=7.2.0,<=8.0.0)", "pytest-subtests", "pytest-xdist", "rich", "ruff (>=0.2.1,<0.3.0)", "scikit-learn", "scipy", "timm", "torchpippy (>=0.2.0)", "tqdm", "transformers"] +quality = ["black (>=23.1,<24.0)", "hf-doc-builder (>=0.3.0)", "ruff (>=0.2.1,<0.3.0)"] +rich = ["rich"] +sagemaker = ["sagemaker"] +test-dev = ["bitsandbytes", "datasets", "diffusers", "evaluate", "scikit-learn", "scipy", "timm", "torchpippy (>=0.2.0)", "tqdm", "transformers"] +test-prod = ["parameterized", "pytest (>=7.2.0,<=8.0.0)", "pytest-subtests", "pytest-xdist"] +test-trackers = ["comet-ml", "dvclive", "tensorboard", "wandb"] +testing = ["bitsandbytes", "datasets", "diffusers", "evaluate", "parameterized", "pytest (>=7.2.0,<=8.0.0)", "pytest-subtests", "pytest-xdist", "scikit-learn", "scipy", "timm", "torchpippy (>=0.2.0)", "tqdm", "transformers"] + +[[package]] +name = "aiohappyeyeballs" +version = "2.3.5" +description = "Happy Eyeballs for asyncio" +optional = false +python-versions = ">=3.8" +files = [ + {file = "aiohappyeyeballs-2.3.5-py3-none-any.whl", hash = "sha256:4d6dea59215537dbc746e93e779caea8178c866856a721c9c660d7a5a7b8be03"}, + {file = "aiohappyeyeballs-2.3.5.tar.gz", hash = "sha256:6fa48b9f1317254f122a07a131a86b71ca6946ca989ce6326fff54a99a920105"}, +] + +[[package]] +name = "aiohttp" +version = "3.10.2" +description = "Async http client/server framework (asyncio)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "aiohttp-3.10.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:95213b3d79c7e387144e9cb7b9d2809092d6ff2c044cb59033aedc612f38fb6d"}, + {file = "aiohttp-3.10.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1aa005f060aff7124cfadaa2493f00a4e28ed41b232add5869e129a2e395935a"}, + {file = "aiohttp-3.10.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:eabe6bf4c199687592f5de4ccd383945f485779c7ffb62a9b9f1f8a3f9756df8"}, + {file = "aiohttp-3.10.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:96e010736fc16d21125c7e2dc5c350cd43c528b85085c04bf73a77be328fe944"}, + {file = "aiohttp-3.10.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99f81f9c1529fd8e03be4a7bd7df32d14b4f856e90ef6e9cbad3415dbfa9166c"}, + {file = "aiohttp-3.10.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d611d1a01c25277bcdea06879afbc11472e33ce842322496b211319aa95441bb"}, + {file = "aiohttp-3.10.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e00191d38156e09e8c81ef3d75c0d70d4f209b8381e71622165f22ef7da6f101"}, + {file = "aiohttp-3.10.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:74c091a5ded6cb81785de2d7a8ab703731f26de910dbe0f3934eabef4ae417cc"}, + {file = "aiohttp-3.10.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:18186a80ec5a701816adbf1d779926e1069392cf18504528d6e52e14b5920525"}, + {file = "aiohttp-3.10.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:5a7ceb2a0d2280f23a02c64cd0afdc922079bb950400c3dd13a1ab2988428aac"}, + {file = "aiohttp-3.10.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8bd7be6ff6c162a60cb8fce65ee879a684fbb63d5466aba3fa5b9288eb04aefa"}, + {file = "aiohttp-3.10.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:fae962b62944eaebff4f4fddcf1a69de919e7b967136a318533d82d93c3c6bd1"}, + {file = "aiohttp-3.10.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a0fde16d284efcacbe15fb0c1013f0967b6c3e379649239d783868230bf1db42"}, + {file = "aiohttp-3.10.2-cp310-cp310-win32.whl", hash = "sha256:f81cd85a0e76ec7b8e2b6636fe02952d35befda4196b8c88f3cec5b4fb512839"}, + {file = "aiohttp-3.10.2-cp310-cp310-win_amd64.whl", hash = "sha256:54ba10eb5a3481c28282eb6afb5f709aedf53cf9c3a31875ffbdc9fc719ffd67"}, + {file = "aiohttp-3.10.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:87fab7f948e407444c2f57088286e00e2ed0003ceaf3d8f8cc0f60544ba61d91"}, + {file = "aiohttp-3.10.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ec6ad66ed660d46503243cbec7b2b3d8ddfa020f984209b3b8ef7d98ce69c3f2"}, + {file = "aiohttp-3.10.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a4be88807283bd96ae7b8e401abde4ca0bab597ba73b5e9a2d98f36d451e9aac"}, + {file = "aiohttp-3.10.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:01c98041f90927c2cbd72c22a164bb816fa3010a047d264969cf82e1d4bcf8d1"}, + {file = "aiohttp-3.10.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:54e36c67e1a9273ecafab18d6693da0fb5ac48fd48417e4548ac24a918c20998"}, + {file = "aiohttp-3.10.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7de3ddb6f424af54535424082a1b5d1ae8caf8256ebd445be68c31c662354720"}, + {file = "aiohttp-3.10.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7dd9c7db94b4692b827ce51dcee597d61a0e4f4661162424faf65106775b40e7"}, + {file = "aiohttp-3.10.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e57e21e1167705f8482ca29cc5d02702208d8bf4aff58f766d94bcd6ead838cd"}, + {file = "aiohttp-3.10.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a1a50e59b720060c29e2951fd9f13c01e1ea9492e5a527b92cfe04dd64453c16"}, + {file = "aiohttp-3.10.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:686c87782481fda5ee6ba572d912a5c26d9f98cc5c243ebd03f95222af3f1b0f"}, + {file = "aiohttp-3.10.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:dafb4abb257c0ed56dc36f4e928a7341b34b1379bd87e5a15ce5d883c2c90574"}, + {file = "aiohttp-3.10.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:494a6f77560e02bd7d1ab579fdf8192390567fc96a603f21370f6e63690b7f3d"}, + {file = "aiohttp-3.10.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6fe8503b1b917508cc68bf44dae28823ac05e9f091021e0c41f806ebbb23f92f"}, + {file = "aiohttp-3.10.2-cp311-cp311-win32.whl", hash = "sha256:4ddb43d06ce786221c0dfd3c91b4892c318eaa36b903f7c4278e7e2fa0dd5102"}, + {file = "aiohttp-3.10.2-cp311-cp311-win_amd64.whl", hash = "sha256:ca2f5abcb0a9a47e56bac173c01e9f6c6e7f27534d91451c5f22e6a35a5a2093"}, + {file = "aiohttp-3.10.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:14eb6b17f6246959fb0b035d4f4ae52caa870c4edfb6170aad14c0de5bfbf478"}, + {file = "aiohttp-3.10.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:465e445ec348d4e4bd349edd8b22db75f025da9d7b6dc1369c48e7935b85581e"}, + {file = "aiohttp-3.10.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:341f8ece0276a828d95b70cd265d20e257f5132b46bf77d759d7f4e0443f2906"}, + {file = "aiohttp-3.10.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c01fbb87b5426381cd9418b3ddcf4fc107e296fa2d3446c18ce6c76642f340a3"}, + {file = "aiohttp-3.10.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2c474af073e1a6763e1c5522bbb2d85ff8318197e4c6c919b8d7886e16213345"}, + {file = "aiohttp-3.10.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d9076810a5621236e29b2204e67a68e1fe317c8727ee4c9abbfbb1083b442c38"}, + {file = "aiohttp-3.10.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e8f515d6859e673940e08de3922b9c4a2249653b0ac181169313bd6e4b1978ac"}, + {file = "aiohttp-3.10.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:655e583afc639bef06f3b2446972c1726007a21003cd0ef57116a123e44601bc"}, + {file = "aiohttp-3.10.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8da9449a575133828cc99985536552ea2dcd690e848f9d41b48d8853a149a959"}, + {file = "aiohttp-3.10.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:19073d57d0feb1865d12361e2a1f5a49cb764bf81a4024a3b608ab521568093a"}, + {file = "aiohttp-3.10.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c8e98e1845805f184d91fda6f9ab93d7c7b0dddf1c07e0255924bfdb151a8d05"}, + {file = "aiohttp-3.10.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:377220a5efde6f9497c5b74649b8c261d3cce8a84cb661be2ed8099a2196400a"}, + {file = "aiohttp-3.10.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:92f7f4a4dc9cdb5980973a74d43cdbb16286dacf8d1896b6c3023b8ba8436f8e"}, + {file = "aiohttp-3.10.2-cp312-cp312-win32.whl", hash = "sha256:9bb2834a6f11d65374ce97d366d6311a9155ef92c4f0cee543b2155d06dc921f"}, + {file = "aiohttp-3.10.2-cp312-cp312-win_amd64.whl", hash = "sha256:518dc3cb37365255708283d1c1c54485bbacccd84f0a0fb87ed8917ba45eda5b"}, + {file = "aiohttp-3.10.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:7f98e70bbbf693086efe4b86d381efad8edac040b8ad02821453083d15ec315f"}, + {file = "aiohttp-3.10.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9f6f0b252a009e98fe84028a4ec48396a948e7a65b8be06ccfc6ef68cf1f614d"}, + {file = "aiohttp-3.10.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9360e3ffc7b23565600e729e8c639c3c50d5520e05fdf94aa2bd859eef12c407"}, + {file = "aiohttp-3.10.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3988044d1635c7821dd44f0edfbe47e9875427464e59d548aece447f8c22800a"}, + {file = "aiohttp-3.10.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:30a9d59da1543a6f1478c3436fd49ec59be3868bca561a33778b4391005e499d"}, + {file = "aiohttp-3.10.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f9f49bdb94809ac56e09a310a62f33e5f22973d6fd351aac72a39cd551e98194"}, + {file = "aiohttp-3.10.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ddfd2dca3f11c365d6857a07e7d12985afc59798458a2fdb2ffa4a0332a3fd43"}, + {file = "aiohttp-3.10.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:685c1508ec97b2cd3e120bfe309a4ff8e852e8a7460f1ef1de00c2c0ed01e33c"}, + {file = "aiohttp-3.10.2-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:49904f38667c44c041a0b44c474b3ae36948d16a0398a8f8cd84e2bb3c42a069"}, + {file = "aiohttp-3.10.2-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:352f3a4e5f11f3241a49b6a48bc5b935fabc35d1165fa0d87f3ca99c1fcca98b"}, + {file = "aiohttp-3.10.2-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:fc61f39b534c5d5903490478a0dd349df397d2284a939aa3cbaa2fb7a19b8397"}, + {file = "aiohttp-3.10.2-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:ad2274e707be37420d0b6c3d26a8115295fe9d8e6e530fa6a42487a8ca3ad052"}, + {file = "aiohttp-3.10.2-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:c836bf3c7512100219fe1123743fd8dd9a2b50dd7cfb0c3bb10d041309acab4b"}, + {file = "aiohttp-3.10.2-cp38-cp38-win32.whl", hash = "sha256:53e8898adda402be03ff164b0878abe2d884e3ea03a4701e6ad55399d84b92dc"}, + {file = "aiohttp-3.10.2-cp38-cp38-win_amd64.whl", hash = "sha256:7cc8f65f5b22304693de05a245b6736b14cb5bc9c8a03da6e2ae9ef15f8b458f"}, + {file = "aiohttp-3.10.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:9dfc906d656e14004c5bc672399c1cccc10db38df2b62a13fb2b6e165a81c316"}, + {file = "aiohttp-3.10.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:91b10208b222ddf655c3a3d5b727879d7163db12b634492df41a9182a76edaae"}, + {file = "aiohttp-3.10.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9fd16b5e1a7bdd14668cd6bde60a2a29b49147a535c74f50d8177d11b38433a7"}, + {file = "aiohttp-3.10.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2bfdda4971bd79201f59adbad24ec2728875237e1c83bba5221284dbbf57bda"}, + {file = "aiohttp-3.10.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:69d73f869cf29e8a373127fc378014e2b17bcfbe8d89134bc6fb06a2f67f3cb3"}, + {file = "aiohttp-3.10.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:df59f8486507c421c0620a2c3dce81fbf1d54018dc20ff4fecdb2c106d6e6abc"}, + {file = "aiohttp-3.10.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0df930015db36b460aa9badbf35eccbc383f00d52d4b6f3de2ccb57d064a6ade"}, + {file = "aiohttp-3.10.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:562b1153ab7f766ee6b8b357ec777a302770ad017cf18505d34f1c088fccc448"}, + {file = "aiohttp-3.10.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:d984db6d855de58e0fde1ef908d48fe9a634cadb3cf715962722b4da1c40619d"}, + {file = "aiohttp-3.10.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:14dc3fcb0d877911d775d511eb617a486a8c48afca0a887276e63db04d3ee920"}, + {file = "aiohttp-3.10.2-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:b52a27a5c97275e254704e1049f4b96a81e67d6205f52fa37a4777d55b0e98ef"}, + {file = "aiohttp-3.10.2-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:cd33d9de8cfd006a0d0fe85f49b4183c57e91d18ffb7e9004ce855e81928f704"}, + {file = "aiohttp-3.10.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1238fc979160bc03a92fff9ad021375ff1c8799c6aacb0d8ea1b357ea40932bb"}, + {file = "aiohttp-3.10.2-cp39-cp39-win32.whl", hash = "sha256:e2f43d238eae4f0b04f58d4c0df4615697d4ca3e9f9b1963d49555a94f0f5a04"}, + {file = "aiohttp-3.10.2-cp39-cp39-win_amd64.whl", hash = "sha256:947847f07a8f81d7b39b2d0202fd73e61962ebe17ac2d8566f260679e467da7b"}, + {file = "aiohttp-3.10.2.tar.gz", hash = "sha256:4d1f694b5d6e459352e5e925a42e05bac66655bfde44d81c59992463d2897014"}, +] + +[package.dependencies] +aiohappyeyeballs = ">=2.3.0" +aiosignal = ">=1.1.2" +async-timeout = {version = ">=4.0,<5.0", markers = "python_version < \"3.11\""} +attrs = ">=17.3.0" +frozenlist = ">=1.1.1" +multidict = ">=4.5,<7.0" +yarl = ">=1.0,<2.0" + +[package.extras] +speedups = ["Brotli", "aiodns (>=3.2.0)", "brotlicffi"] + +[[package]] +name = "aiosignal" +version = "1.3.1" +description = "aiosignal: a list of registered asynchronous callbacks" +optional = false +python-versions = ">=3.7" +files = [ + {file = "aiosignal-1.3.1-py3-none-any.whl", hash = "sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17"}, + {file = "aiosignal-1.3.1.tar.gz", hash = "sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc"}, +] + +[package.dependencies] +frozenlist = ">=1.1.0" + +[[package]] +name = "antlr4-python3-runtime" +version = "4.9.3" +description = "ANTLR 4.9.3 runtime for Python 3.7" +optional = false +python-versions = "*" +files = [ + {file = "antlr4-python3-runtime-4.9.3.tar.gz", hash = "sha256:f224469b4168294902bb1efa80a8bf7855f24c99aef99cbefc1bcd3cce77881b"}, +] + +[[package]] +name = "appnope" +version = "0.1.4" +description = "Disable App Nap on macOS >= 10.9" +optional = false +python-versions = ">=3.6" +files = [ + {file = "appnope-0.1.4-py2.py3-none-any.whl", hash = "sha256:502575ee11cd7a28c0205f379b525beefebab9d161b7c964670864014ed7213c"}, + {file = "appnope-0.1.4.tar.gz", hash = "sha256:1de3860566df9caf38f01f86f65e0e13e379af54f9e4bee1e66b48f2efffd1ee"}, +] + +[[package]] +name = "astroid" +version = "3.2.4" +description = "An abstract syntax tree for Python with inference support." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "astroid-3.2.4-py3-none-any.whl", hash = "sha256:413658a61eeca6202a59231abb473f932038fbcbf1666587f66d482083413a25"}, + {file = "astroid-3.2.4.tar.gz", hash = "sha256:0e14202810b30da1b735827f78f5157be2bbd4a7a59b7707ca0bfc2fb4c0063a"}, +] + +[package.dependencies] +typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.11\""} + +[[package]] +name = "asttokens" +version = "2.4.1" +description = "Annotate AST trees with source code positions" +optional = false +python-versions = "*" +files = [ + {file = "asttokens-2.4.1-py2.py3-none-any.whl", hash = "sha256:051ed49c3dcae8913ea7cd08e46a606dba30b79993209636c4875bc1d637bc24"}, + {file = "asttokens-2.4.1.tar.gz", hash = "sha256:b03869718ba9a6eb027e134bfdf69f38a236d681c83c160d510768af11254ba0"}, +] + +[package.dependencies] +six = ">=1.12.0" + +[package.extras] +astroid = ["astroid (>=1,<2)", "astroid (>=2,<4)"] +test = ["astroid (>=1,<2)", "astroid (>=2,<4)", "pytest"] + +[[package]] +name = "async-timeout" +version = "4.0.3" +description = "Timeout context manager for asyncio programs" +optional = false +python-versions = ">=3.7" +files = [ + {file = "async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f"}, + {file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"}, +] + +[[package]] +name = "attrs" +version = "24.2.0" +description = "Classes Without Boilerplate" +optional = false +python-versions = ">=3.7" +files = [ + {file = "attrs-24.2.0-py3-none-any.whl", hash = "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2"}, + {file = "attrs-24.2.0.tar.gz", hash = "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346"}, +] + +[package.extras] +benchmark = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +cov = ["cloudpickle", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier (<24.7)"] +tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"] + +[[package]] +name = "autopep8" +version = "2.3.1" +description = "A tool that automatically formats Python code to conform to the PEP 8 style guide" +optional = false +python-versions = ">=3.8" +files = [ + {file = "autopep8-2.3.1-py2.py3-none-any.whl", hash = "sha256:a203fe0fcad7939987422140ab17a930f684763bf7335bdb6709991dd7ef6c2d"}, + {file = "autopep8-2.3.1.tar.gz", hash = "sha256:8d6c87eba648fdcfc83e29b788910b8643171c395d9c4bcf115ece035b9c9dda"}, +] + +[package.dependencies] +pycodestyle = ">=2.12.0" +tomli = {version = "*", markers = "python_version < \"3.11\""} + +[[package]] +name = "av" +version = "12.3.0" +description = "Pythonic bindings for FFmpeg's libraries." +optional = false +python-versions = ">=3.8" +files = [ + {file = "av-12.3.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:b3b1fe6b5ab9af2d09dcdcc5473a3523f7162c3fa0c6b3c379b697fede1e88a5"}, + {file = "av-12.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b5f92ba67dca9bac8ce955b09d41e7e92977199adbd0f2aff02653bb40b0ac16"}, + {file = "av-12.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3389eebd1f5bb36ebfaa8441c65c14d7433b354d91f9dbb08a6e6225d16a7226"}, + {file = "av-12.3.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:385b27638bc56fd1560be3b9e86b5cc843cae931503a02e6e504c0357176873e"}, + {file = "av-12.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0220fce2a62d71cc5e89617419b6224ddb43f1753b00f68b5c9af8b5f41d38c9"}, + {file = "av-12.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:8328c90f783b3392279a2d3a79789267691f5e5f7c4a160990a41194d268ec59"}, + {file = "av-12.3.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:cc06a806419fddc7102150ffe353c7d96b99b95fd12864280c91c851603fd4cb"}, + {file = "av-12.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8e2130ff622a574d3d5d6e88ac335efcdd98c375bb341f87d9fe540830a746f5"}, + {file = "av-12.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e8b9bd99f916ff4d1278654e94658e6ace7ca60f6321f254d09c8cd81d9095b"}, + {file = "av-12.3.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9e375d1d89a5c6edfd9f66701fdb6cc9161cc1ff99d15ff0bda21ee1ad38e9e0"}, + {file = "av-12.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef9066fd8d86548e12d587cbfe7b852159e48ff3c732271c3032668d4bd7c599"}, + {file = "av-12.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:bfaa9864560e43d45d254ed95f70ab1aab24a2fa0cc35ac99eef362f1453bec0"}, + {file = "av-12.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5174e995772ebe33561980dca625f830aea8d39a4338728dedb41ae7dc2605af"}, + {file = "av-12.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:028d8b40308536f740dace3efd0178eb96825b414897c9594fb74136532901cb"}, + {file = "av-12.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b030791ecc6185776d832d19ce196f61daf3e17e591a9bb6fd181280e1754138"}, + {file = "av-12.3.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a3703a35481fda5798a27bf6208c1ec3b61c18931625771fb3c9fd870539c7d7"}, + {file = "av-12.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32f3eef56b2df289db6105f9fe2ebc9a8134a8adbd62190daeb8e22c4ff47794"}, + {file = "av-12.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:62d036ee8321d67190887012c3dbcd1ad83248603cc29ea75fbb75835b8d6e6e"}, + {file = "av-12.3.0-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:d04d908febe4673311cae47b3f43d1c4858177fb5028fd3bb1b9fb46291e9748"}, + {file = "av-12.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8f380ee818f28435daa5ffc10d7f6e3854f3019bafb210dea5977a7292ae2467"}, + {file = "av-12.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebbfe391ee4d4d4dd1f8ec3969ced65362a811d3edb210933ce46c946f6e9263"}, + {file = "av-12.3.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:20df6c5b71964adb05b353439f1e00b06e32526b2feaf1c5ff07a7a7f2feca38"}, + {file = "av-12.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1a6512a12ace56d17ffb8a4909db724e2b6cc968ab8370ae75e7743387e86d1"}, + {file = "av-12.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:7faadac791efee412f17309a3471d3a64f84a1761c3dfb360b8eda26dfc60f70"}, + {file = "av-12.3.0-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:6d29265257c1b6183d96c5e93ab563ecce029574d99b31d361eeb5bfcebe2a0b"}, + {file = "av-12.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:508dd1d104bc1e4df18949ab4100e3d7bedf302e21ea417e8b91e2f9abfa0612"}, + {file = "av-12.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ecbf44b74490febb8ff3e5ca63c06c0e601f7633af6ec5308fe40431b3735ea1"}, + {file = "av-12.3.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5f97fa62d97f5aa5312fb85e45374b878c81b9cda2a210f61cfd43f269895786"}, + {file = "av-12.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01115c2b53585e26d6764e2aa66e7a0f0d7b4ab80f96e3dc931cc9029a69f975"}, + {file = "av-12.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:410f49fa7f6d817b1a311b375fb9f8c7c8149607cb0f7ae82ec55dbf82ce85e8"}, + {file = "av-12.3.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e47ba817fcd46c9f2c94d638abcdeda120adedcd09605984a5cee844f739a833"}, + {file = "av-12.3.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:b456cbb7ddd252f0f2db06a09dc10ade201e82e0eb8d3a7b609689907b2802df"}, + {file = "av-12.3.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50ccb92605d59732d2a2923786a5dba746a98c5fd6b4d30a5975785673c42c9e"}, + {file = "av-12.3.0-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:061b15203f22e95c60b1cc14702618acbf18e976cf3144298e2f6dc89b7aa993"}, + {file = "av-12.3.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65849ca4e54f2d50ed263ab488ef051bd973cbdbe2a7c947b31ff965bb7bfddd"}, + {file = "av-12.3.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:18e915ca9001f9491cb4091fe6ca0744a48da20412be44f71bbfc641efbf518f"}, + {file = "av-12.3.0-pp38-pypy38_pp73-macosx_10_13_x86_64.whl", hash = "sha256:9b93e1e4d8f5f46f3d21970a2d06b06fef8e36e3fd3fd78c2fed7c8f6b46a89c"}, + {file = "av-12.3.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:bc38c84afd5d38a5d6429dd687f69b09b563bca52c44d8cc44acea1dd6035184"}, + {file = "av-12.3.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf0cc3c665365a7c5bc4bfa83ad6096660648060cbf411466e69692eba6dde9d"}, + {file = "av-12.3.0-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:126426897852e974781755209747ed7f9888ad3ef17fe274e0fe98fd5659568d"}, + {file = "av-12.3.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3bdcd36bccf2d62655a4429c84855f0c99da42529c1ac8da391d8efe83d0afe"}, + {file = "av-12.3.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:db313fce97b1c3bb50eb1f9483c705c0e51733b105a81c61c9d0946552185f2b"}, + {file = "av-12.3.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:21303fa04cad5b21e6671d3ef54c80262be632efd79536ead8179f08529820c0"}, + {file = "av-12.3.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:b8bfaa314bc75d492acbe02592ea6bbcf8674776b645a941aeda00ebaf70c1a9"}, + {file = "av-12.3.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c0a34c2872a40daad6d9f43169caf977687b28c757dd49032797d2535c062db"}, + {file = "av-12.3.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:15d2348be3db7432774febca59c6c5b92f292c521b586cdffbe3da2c9f2bde59"}, + {file = "av-12.3.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4d858cd2a34e21e373be0bc4b79e996c32b2bc92ab7494d4cd26f33370e045fd"}, + {file = "av-12.3.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:d39b24186794128da924e032f650a37f69ef2c7b10a66749426b655082d68a75"}, + {file = "av-12.3.0.tar.gz", hash = "sha256:04b1892562aff3277efc79f32bd8f1d0cbb64ed011241cb3e96f9ad471816c22"}, +] + +[[package]] +name = "black" +version = "24.8.0" +description = "The uncompromising code formatter." +optional = false +python-versions = ">=3.8" +files = [ + {file = "black-24.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:09cdeb74d494ec023ded657f7092ba518e8cf78fa8386155e4a03fdcc44679e6"}, + {file = "black-24.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:81c6742da39f33b08e791da38410f32e27d632260e599df7245cccee2064afeb"}, + {file = "black-24.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:707a1ca89221bc8a1a64fb5e15ef39cd755633daa672a9db7498d1c19de66a42"}, + {file = "black-24.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:d6417535d99c37cee4091a2f24eb2b6d5ec42b144d50f1f2e436d9fe1916fe1a"}, + {file = "black-24.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fb6e2c0b86bbd43dee042e48059c9ad7830abd5c94b0bc518c0eeec57c3eddc1"}, + {file = "black-24.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:837fd281f1908d0076844bc2b801ad2d369c78c45cf800cad7b61686051041af"}, + {file = "black-24.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:62e8730977f0b77998029da7971fa896ceefa2c4c4933fcd593fa599ecbf97a4"}, + {file = "black-24.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:72901b4913cbac8972ad911dc4098d5753704d1f3c56e44ae8dce99eecb0e3af"}, + {file = "black-24.8.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:7c046c1d1eeb7aea9335da62472481d3bbf3fd986e093cffd35f4385c94ae368"}, + {file = "black-24.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:649f6d84ccbae73ab767e206772cc2d7a393a001070a4c814a546afd0d423aed"}, + {file = "black-24.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2b59b250fdba5f9a9cd9d0ece6e6d993d91ce877d121d161e4698af3eb9c1018"}, + {file = "black-24.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:6e55d30d44bed36593c3163b9bc63bf58b3b30e4611e4d88a0c3c239930ed5b2"}, + {file = "black-24.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:505289f17ceda596658ae81b61ebbe2d9b25aa78067035184ed0a9d855d18afd"}, + {file = "black-24.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b19c9ad992c7883ad84c9b22aaa73562a16b819c1d8db7a1a1a49fb7ec13c7d2"}, + {file = "black-24.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1f13f7f386f86f8121d76599114bb8c17b69d962137fc70efe56137727c7047e"}, + {file = "black-24.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:f490dbd59680d809ca31efdae20e634f3fae27fba3ce0ba3208333b713bc3920"}, + {file = "black-24.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:eab4dd44ce80dea27dc69db40dab62d4ca96112f87996bca68cd75639aeb2e4c"}, + {file = "black-24.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3c4285573d4897a7610054af5a890bde7c65cb466040c5f0c8b732812d7f0e5e"}, + {file = "black-24.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e84e33b37be070ba135176c123ae52a51f82306def9f7d063ee302ecab2cf47"}, + {file = "black-24.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:73bbf84ed136e45d451a260c6b73ed674652f90a2b3211d6a35e78054563a9bb"}, + {file = "black-24.8.0-py3-none-any.whl", hash = "sha256:972085c618ee94f402da1af548a4f218c754ea7e5dc70acb168bfaca4c2542ed"}, + {file = "black-24.8.0.tar.gz", hash = "sha256:2500945420b6784c38b9ee885af039f5e7471ef284ab03fa35ecdde4688cd83f"}, +] + +[package.dependencies] +click = ">=8.0.0" +mypy-extensions = ">=0.4.3" +packaging = ">=22.0" +pathspec = ">=0.9.0" +platformdirs = ">=2" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] + +[[package]] +name = "blacken-docs" +version = "1.18.0" +description = "Run Black on Python code blocks in documentation files." +optional = false +python-versions = ">=3.8" +files = [ + {file = "blacken_docs-1.18.0-py3-none-any.whl", hash = "sha256:64f592246784131e9f84dad1db397f44eeddc77fdf01726bab920a3f00a3815c"}, + {file = "blacken_docs-1.18.0.tar.gz", hash = "sha256:47bed628679d008a8eb55d112df950582e68d0f57615223929e366348d935444"}, +] + +[package.dependencies] +black = ">=22.1" + +[[package]] +name = "boolean-py" +version = "4.0" +description = "Define boolean algebras, create and parse boolean expressions and create custom boolean DSL." +optional = false +python-versions = "*" +files = [ + {file = "boolean.py-4.0-py3-none-any.whl", hash = "sha256:2876f2051d7d6394a531d82dc6eb407faa0b01a0a0b3083817ccd7323b8d96bd"}, + {file = "boolean.py-4.0.tar.gz", hash = "sha256:17b9a181630e43dde1851d42bef546d616d5d9b4480357514597e78b203d06e4"}, +] + +[[package]] +name = "cachecontrol" +version = "0.14.0" +description = "httplib2 caching for requests" +optional = false +python-versions = ">=3.7" +files = [ + {file = "cachecontrol-0.14.0-py3-none-any.whl", hash = "sha256:f5bf3f0620c38db2e5122c0726bdebb0d16869de966ea6a2befe92470b740ea0"}, + {file = "cachecontrol-0.14.0.tar.gz", hash = "sha256:7db1195b41c81f8274a7bbd97c956f44e8348265a1bc7641c37dfebc39f0c938"}, +] + +[package.dependencies] +filelock = {version = ">=3.8.0", optional = true, markers = "extra == \"filecache\""} +msgpack = ">=0.5.2,<2.0.0" +requests = ">=2.16.0" + +[package.extras] +dev = ["CacheControl[filecache,redis]", "black", "build", "cherrypy", "furo", "mypy", "pytest", "pytest-cov", "sphinx", "sphinx-copybutton", "tox", "types-redis", "types-requests"] +filecache = ["filelock (>=3.8.0)"] +redis = ["redis (>=2.10.5)"] + +[[package]] +name = "certifi" +version = "2024.7.4" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2024.7.4-py3-none-any.whl", hash = "sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90"}, + {file = "certifi-2024.7.4.tar.gz", hash = "sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b"}, +] + +[[package]] +name = "cffi" +version = "1.17.0" +description = "Foreign Function Interface for Python calling C code." +optional = false +python-versions = ">=3.8" +files = [ + {file = "cffi-1.17.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f9338cc05451f1942d0d8203ec2c346c830f8e86469903d5126c1f0a13a2bcbb"}, + {file = "cffi-1.17.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a0ce71725cacc9ebf839630772b07eeec220cbb5f03be1399e0457a1464f8e1a"}, + {file = "cffi-1.17.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c815270206f983309915a6844fe994b2fa47e5d05c4c4cef267c3b30e34dbe42"}, + {file = "cffi-1.17.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d6bdcd415ba87846fd317bee0774e412e8792832e7805938987e4ede1d13046d"}, + {file = "cffi-1.17.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8a98748ed1a1df4ee1d6f927e151ed6c1a09d5ec21684de879c7ea6aa96f58f2"}, + {file = "cffi-1.17.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0a048d4f6630113e54bb4b77e315e1ba32a5a31512c31a273807d0027a7e69ab"}, + {file = "cffi-1.17.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24aa705a5f5bd3a8bcfa4d123f03413de5d86e497435693b638cbffb7d5d8a1b"}, + {file = "cffi-1.17.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:856bf0924d24e7f93b8aee12a3a1095c34085600aa805693fb7f5d1962393206"}, + {file = "cffi-1.17.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:4304d4416ff032ed50ad6bb87416d802e67139e31c0bde4628f36a47a3164bfa"}, + {file = "cffi-1.17.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:331ad15c39c9fe9186ceaf87203a9ecf5ae0ba2538c9e898e3a6967e8ad3db6f"}, + {file = "cffi-1.17.0-cp310-cp310-win32.whl", hash = "sha256:669b29a9eca6146465cc574659058ed949748f0809a2582d1f1a324eb91054dc"}, + {file = "cffi-1.17.0-cp310-cp310-win_amd64.whl", hash = "sha256:48b389b1fd5144603d61d752afd7167dfd205973a43151ae5045b35793232aa2"}, + {file = "cffi-1.17.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c5d97162c196ce54af6700949ddf9409e9833ef1003b4741c2b39ef46f1d9720"}, + {file = "cffi-1.17.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5ba5c243f4004c750836f81606a9fcb7841f8874ad8f3bf204ff5e56332b72b9"}, + {file = "cffi-1.17.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bb9333f58fc3a2296fb1d54576138d4cf5d496a2cc118422bd77835e6ae0b9cb"}, + {file = "cffi-1.17.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:435a22d00ec7d7ea533db494da8581b05977f9c37338c80bc86314bec2619424"}, + {file = "cffi-1.17.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d1df34588123fcc88c872f5acb6f74ae59e9d182a2707097f9e28275ec26a12d"}, + {file = "cffi-1.17.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:df8bb0010fdd0a743b7542589223a2816bdde4d94bb5ad67884348fa2c1c67e8"}, + {file = "cffi-1.17.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8b5b9712783415695663bd463990e2f00c6750562e6ad1d28e072a611c5f2a6"}, + {file = "cffi-1.17.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ffef8fd58a36fb5f1196919638f73dd3ae0db1a878982b27a9a5a176ede4ba91"}, + {file = "cffi-1.17.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4e67d26532bfd8b7f7c05d5a766d6f437b362c1bf203a3a5ce3593a645e870b8"}, + {file = "cffi-1.17.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:45f7cd36186db767d803b1473b3c659d57a23b5fa491ad83c6d40f2af58e4dbb"}, + {file = "cffi-1.17.0-cp311-cp311-win32.whl", hash = "sha256:a9015f5b8af1bb6837a3fcb0cdf3b874fe3385ff6274e8b7925d81ccaec3c5c9"}, + {file = "cffi-1.17.0-cp311-cp311-win_amd64.whl", hash = "sha256:b50aaac7d05c2c26dfd50c3321199f019ba76bb650e346a6ef3616306eed67b0"}, + {file = "cffi-1.17.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aec510255ce690d240f7cb23d7114f6b351c733a74c279a84def763660a2c3bc"}, + {file = "cffi-1.17.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2770bb0d5e3cc0e31e7318db06efcbcdb7b31bcb1a70086d3177692a02256f59"}, + {file = "cffi-1.17.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:db9a30ec064129d605d0f1aedc93e00894b9334ec74ba9c6bdd08147434b33eb"}, + {file = "cffi-1.17.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a47eef975d2b8b721775a0fa286f50eab535b9d56c70a6e62842134cf7841195"}, + {file = "cffi-1.17.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f3e0992f23bbb0be00a921eae5363329253c3b86287db27092461c887b791e5e"}, + {file = "cffi-1.17.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6107e445faf057c118d5050560695e46d272e5301feffda3c41849641222a828"}, + {file = "cffi-1.17.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb862356ee9391dc5a0b3cbc00f416b48c1b9a52d252d898e5b7696a5f9fe150"}, + {file = "cffi-1.17.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c1c13185b90bbd3f8b5963cd8ce7ad4ff441924c31e23c975cb150e27c2bf67a"}, + {file = "cffi-1.17.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:17c6d6d3260c7f2d94f657e6872591fe8733872a86ed1345bda872cfc8c74885"}, + {file = "cffi-1.17.0-cp312-cp312-win32.whl", hash = "sha256:c3b8bd3133cd50f6b637bb4322822c94c5ce4bf0d724ed5ae70afce62187c492"}, + {file = "cffi-1.17.0-cp312-cp312-win_amd64.whl", hash = "sha256:dca802c8db0720ce1c49cce1149ff7b06e91ba15fa84b1d59144fef1a1bc7ac2"}, + {file = "cffi-1.17.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6ce01337d23884b21c03869d2f68c5523d43174d4fc405490eb0091057943118"}, + {file = "cffi-1.17.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cab2eba3830bf4f6d91e2d6718e0e1c14a2f5ad1af68a89d24ace0c6b17cced7"}, + {file = "cffi-1.17.0-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:14b9cbc8f7ac98a739558eb86fabc283d4d564dafed50216e7f7ee62d0d25377"}, + {file = "cffi-1.17.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b00e7bcd71caa0282cbe3c90966f738e2db91e64092a877c3ff7f19a1628fdcb"}, + {file = "cffi-1.17.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:41f4915e09218744d8bae14759f983e466ab69b178de38066f7579892ff2a555"}, + {file = "cffi-1.17.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e4760a68cab57bfaa628938e9c2971137e05ce48e762a9cb53b76c9b569f1204"}, + {file = "cffi-1.17.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:011aff3524d578a9412c8b3cfaa50f2c0bd78e03eb7af7aa5e0df59b158efb2f"}, + {file = "cffi-1.17.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:a003ac9edc22d99ae1286b0875c460351f4e101f8c9d9d2576e78d7e048f64e0"}, + {file = "cffi-1.17.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ef9528915df81b8f4c7612b19b8628214c65c9b7f74db2e34a646a0a2a0da2d4"}, + {file = "cffi-1.17.0-cp313-cp313-win32.whl", hash = "sha256:70d2aa9fb00cf52034feac4b913181a6e10356019b18ef89bc7c12a283bf5f5a"}, + {file = "cffi-1.17.0-cp313-cp313-win_amd64.whl", hash = "sha256:b7b6ea9e36d32582cda3465f54c4b454f62f23cb083ebc7a94e2ca6ef011c3a7"}, + {file = "cffi-1.17.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:964823b2fc77b55355999ade496c54dde161c621cb1f6eac61dc30ed1b63cd4c"}, + {file = "cffi-1.17.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:516a405f174fd3b88829eabfe4bb296ac602d6a0f68e0d64d5ac9456194a5b7e"}, + {file = "cffi-1.17.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dec6b307ce928e8e112a6bb9921a1cb00a0e14979bf28b98e084a4b8a742bd9b"}, + {file = "cffi-1.17.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e4094c7b464cf0a858e75cd14b03509e84789abf7b79f8537e6a72152109c76e"}, + {file = "cffi-1.17.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2404f3de742f47cb62d023f0ba7c5a916c9c653d5b368cc966382ae4e57da401"}, + {file = "cffi-1.17.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3aa9d43b02a0c681f0bfbc12d476d47b2b2b6a3f9287f11ee42989a268a1833c"}, + {file = "cffi-1.17.0-cp38-cp38-win32.whl", hash = "sha256:0bb15e7acf8ab35ca8b24b90af52c8b391690ef5c4aec3d31f38f0d37d2cc499"}, + {file = "cffi-1.17.0-cp38-cp38-win_amd64.whl", hash = "sha256:93a7350f6706b31f457c1457d3a3259ff9071a66f312ae64dc024f049055f72c"}, + {file = "cffi-1.17.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1a2ddbac59dc3716bc79f27906c010406155031a1c801410f1bafff17ea304d2"}, + {file = "cffi-1.17.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6327b572f5770293fc062a7ec04160e89741e8552bf1c358d1a23eba68166759"}, + {file = "cffi-1.17.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbc183e7bef690c9abe5ea67b7b60fdbca81aa8da43468287dae7b5c046107d4"}, + {file = "cffi-1.17.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5bdc0f1f610d067c70aa3737ed06e2726fd9d6f7bfee4a351f4c40b6831f4e82"}, + {file = "cffi-1.17.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6d872186c1617d143969defeadac5a904e6e374183e07977eedef9c07c8953bf"}, + {file = "cffi-1.17.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0d46ee4764b88b91f16661a8befc6bfb24806d885e27436fdc292ed7e6f6d058"}, + {file = "cffi-1.17.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f76a90c345796c01d85e6332e81cab6d70de83b829cf1d9762d0a3da59c7932"}, + {file = "cffi-1.17.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0e60821d312f99d3e1569202518dddf10ae547e799d75aef3bca3a2d9e8ee693"}, + {file = "cffi-1.17.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:eb09b82377233b902d4c3fbeeb7ad731cdab579c6c6fda1f763cd779139e47c3"}, + {file = "cffi-1.17.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:24658baf6224d8f280e827f0a50c46ad819ec8ba380a42448e24459daf809cf4"}, + {file = "cffi-1.17.0-cp39-cp39-win32.whl", hash = "sha256:0fdacad9e0d9fc23e519efd5ea24a70348305e8d7d85ecbb1a5fa66dc834e7fb"}, + {file = "cffi-1.17.0-cp39-cp39-win_amd64.whl", hash = "sha256:7cbc78dc018596315d4e7841c8c3a7ae31cc4d638c9b627f87d52e8abaaf2d29"}, + {file = "cffi-1.17.0.tar.gz", hash = "sha256:f3157624b7558b914cb039fd1af735e5e8049a87c817cc215109ad1c8779df76"}, +] + +[package.dependencies] +pycparser = "*" + +[[package]] +name = "cfgv" +version = "3.4.0" +description = "Validate configuration and produce human readable error messages." +optional = false +python-versions = ">=3.8" +files = [ + {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"}, + {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, +] + +[[package]] +name = "charset-normalizer" +version = "3.3.2" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, + {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, +] + +[[package]] +name = "click" +version = "8.1.7" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "cloudpickle" +version = "3.0.0" +description = "Pickler class to extend the standard pickle.Pickler functionality" +optional = false +python-versions = ">=3.8" +files = [ + {file = "cloudpickle-3.0.0-py3-none-any.whl", hash = "sha256:246ee7d0c295602a036e86369c77fecda4ab17b506496730f2f576d9016fd9c7"}, + {file = "cloudpickle-3.0.0.tar.gz", hash = "sha256:996d9a482c6fb4f33c1a35335cf8afd065d2a56e973270364840712d9131a882"}, +] + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +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.2.2" +description = "Jupyter Python Comm implementation, for usage in ipykernel, xeus-python etc." +optional = false +python-versions = ">=3.8" +files = [ + {file = "comm-0.2.2-py3-none-any.whl", hash = "sha256:e6fb86cb70ff661ee8c9c14e7d36d6de3b4066f1441be4063df9c5009f0a64d3"}, + {file = "comm-0.2.2.tar.gz", hash = "sha256:3fd7a84065306e07bea1773df6eb8282de51ba82f77c72f9c85716ab11fe980e"}, +] + +[package.dependencies] +traitlets = ">=4" + +[package.extras] +test = ["pytest"] + +[[package]] +name = "cyclonedx-python-lib" +version = "7.5.1" +description = "Python library for CycloneDX" +optional = false +python-versions = "<4.0,>=3.8" +files = [ + {file = "cyclonedx_python_lib-7.5.1-py3-none-any.whl", hash = "sha256:9fc2c2e5facfd9530ede1f4525c903d29d91945688c5689b6d5fab46381dcab9"}, + {file = "cyclonedx_python_lib-7.5.1.tar.gz", hash = "sha256:00cfe1e58452698650ae08b8f4389f7b1ec203a3e1c50cbf6ca6d320941dfb3f"}, +] + +[package.dependencies] +license-expression = ">=30,<31" +packageurl-python = ">=0.11,<2" +py-serializable = ">=1.1.0,<2.0.0" +sortedcontainers = ">=2.4.0,<3.0.0" + +[package.extras] +json-validation = ["jsonschema[format] (>=4.18,<5.0)"] +validation = ["jsonschema[format] (>=4.18,<5.0)", "lxml (>=4,<6)"] +xml-validation = ["lxml (>=4,<6)"] + +[[package]] +name = "debugpy" +version = "1.8.5" +description = "An implementation of the Debug Adapter Protocol for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "debugpy-1.8.5-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:7e4d594367d6407a120b76bdaa03886e9eb652c05ba7f87e37418426ad2079f7"}, + {file = "debugpy-1.8.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4413b7a3ede757dc33a273a17d685ea2b0c09dbd312cc03f5534a0fd4d40750a"}, + {file = "debugpy-1.8.5-cp310-cp310-win32.whl", hash = "sha256:dd3811bd63632bb25eda6bd73bea8e0521794cda02be41fa3160eb26fc29e7ed"}, + {file = "debugpy-1.8.5-cp310-cp310-win_amd64.whl", hash = "sha256:b78c1250441ce893cb5035dd6f5fc12db968cc07f91cc06996b2087f7cefdd8e"}, + {file = "debugpy-1.8.5-cp311-cp311-macosx_12_0_universal2.whl", hash = "sha256:606bccba19f7188b6ea9579c8a4f5a5364ecd0bf5a0659c8a5d0e10dcee3032a"}, + {file = "debugpy-1.8.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db9fb642938a7a609a6c865c32ecd0d795d56c1aaa7a7a5722d77855d5e77f2b"}, + {file = "debugpy-1.8.5-cp311-cp311-win32.whl", hash = "sha256:4fbb3b39ae1aa3e5ad578f37a48a7a303dad9a3d018d369bc9ec629c1cfa7408"}, + {file = "debugpy-1.8.5-cp311-cp311-win_amd64.whl", hash = "sha256:345d6a0206e81eb68b1493ce2fbffd57c3088e2ce4b46592077a943d2b968ca3"}, + {file = "debugpy-1.8.5-cp312-cp312-macosx_12_0_universal2.whl", hash = "sha256:5b5c770977c8ec6c40c60d6f58cacc7f7fe5a45960363d6974ddb9b62dbee156"}, + {file = "debugpy-1.8.5-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0a65b00b7cdd2ee0c2cf4c7335fef31e15f1b7056c7fdbce9e90193e1a8c8cb"}, + {file = "debugpy-1.8.5-cp312-cp312-win32.whl", hash = "sha256:c9f7c15ea1da18d2fcc2709e9f3d6de98b69a5b0fff1807fb80bc55f906691f7"}, + {file = "debugpy-1.8.5-cp312-cp312-win_amd64.whl", hash = "sha256:28ced650c974aaf179231668a293ecd5c63c0a671ae6d56b8795ecc5d2f48d3c"}, + {file = "debugpy-1.8.5-cp38-cp38-macosx_12_0_x86_64.whl", hash = "sha256:3df6692351172a42af7558daa5019651f898fc67450bf091335aa8a18fbf6f3a"}, + {file = "debugpy-1.8.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1cd04a73eb2769eb0bfe43f5bfde1215c5923d6924b9b90f94d15f207a402226"}, + {file = "debugpy-1.8.5-cp38-cp38-win32.whl", hash = "sha256:8f913ee8e9fcf9d38a751f56e6de12a297ae7832749d35de26d960f14280750a"}, + {file = "debugpy-1.8.5-cp38-cp38-win_amd64.whl", hash = "sha256:a697beca97dad3780b89a7fb525d5e79f33821a8bc0c06faf1f1289e549743cf"}, + {file = "debugpy-1.8.5-cp39-cp39-macosx_12_0_x86_64.whl", hash = "sha256:0a1029a2869d01cb777216af8c53cda0476875ef02a2b6ff8b2f2c9a4b04176c"}, + {file = "debugpy-1.8.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e84c276489e141ed0b93b0af648eef891546143d6a48f610945416453a8ad406"}, + {file = "debugpy-1.8.5-cp39-cp39-win32.whl", hash = "sha256:ad84b7cde7fd96cf6eea34ff6c4a1b7887e0fe2ea46e099e53234856f9d99a34"}, + {file = "debugpy-1.8.5-cp39-cp39-win_amd64.whl", hash = "sha256:7b0fe36ed9d26cb6836b0a51453653f8f2e347ba7348f2bbfe76bfeb670bfb1c"}, + {file = "debugpy-1.8.5-py2.py3-none-any.whl", hash = "sha256:55919dce65b471eff25901acf82d328bbd5b833526b6c1364bd5133754777a44"}, + {file = "debugpy-1.8.5.zip", hash = "sha256:b2112cfeb34b4507399d298fe7023a16656fc553ed5246536060ca7bd0e668d0"}, +] + +[[package]] +name = "decorator" +version = "5.1.1" +description = "Decorators for Humans" +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 = "decord" +version = "0.6.0" +description = "Decord Video Loader" +optional = false +python-versions = "*" +files = [ + {file = "decord-0.6.0-cp36-cp36m-macosx_10_15_x86_64.whl", hash = "sha256:85ef90d2f872384657d7774cc486c237c5b12df62d4ac5cb5c8d6001fa611323"}, + {file = "decord-0.6.0-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:9c20674964fb1490c677bd911d2023d2a09fec7a58a4bb0b7ddf1ccc269f107a"}, + {file = "decord-0.6.0-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:a0eb1258beade34dceb29d97856a7764d179db1b5182899b61874f3418a1abc8"}, + {file = "decord-0.6.0-py3-none-manylinux2010_x86_64.whl", hash = "sha256:51997f20be8958e23b7c4061ba45d0efcd86bffd5fe81c695d0befee0d442976"}, + {file = "decord-0.6.0-py3-none-win_amd64.whl", hash = "sha256:02665d7c4f1193a330205a791bc128f7e108eb6ae5b67144437a02f700943bad"}, +] + +[package.dependencies] +numpy = ">=1.14.0" + +[[package]] +name = "defusedxml" +version = "0.7.1" +description = "XML bomb protection for Python stdlib modules" +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 = "dill" +version = "0.3.8" +description = "serialize all of Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7"}, + {file = "dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca"}, +] + +[package.extras] +graph = ["objgraph (>=1.7.2)"] +profile = ["gprof2dot (>=2022.7.29)"] + +[[package]] +name = "distlib" +version = "0.3.8" +description = "Distribution utilities" +optional = false +python-versions = "*" +files = [ + {file = "distlib-0.3.8-py2.py3-none-any.whl", hash = "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784"}, + {file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"}, +] + +[[package]] +name = "docker-pycreds" +version = "0.4.0" +description = "Python bindings for the docker credentials store API" +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 = "exceptiongroup" +version = "1.2.2" +description = "Backport of PEP 654 (exception groups)" +optional = false +python-versions = ">=3.7" +files = [ + {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, + {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, +] + +[package.extras] +test = ["pytest (>=6)"] + +[[package]] +name = "executing" +version = "2.0.1" +description = "Get the currently executing AST node of a frame, and other information" +optional = false +python-versions = ">=3.5" +files = [ + {file = "executing-2.0.1-py2.py3-none-any.whl", hash = "sha256:eac49ca94516ccc753f9fb5ce82603156e590b27525a8bc32cce8ae302eb61bc"}, + {file = "executing-2.0.1.tar.gz", hash = "sha256:35afe2ce3affba8ee97f2d69927fa823b08b472b7b994e36a52a964b93d16147"}, +] + +[package.extras] +tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich"] + +[[package]] +name = "fastjsonschema" +version = "2.20.0" +description = "Fastest Python implementation of JSON schema" +optional = false +python-versions = "*" +files = [ + {file = "fastjsonschema-2.20.0-py3-none-any.whl", hash = "sha256:5875f0b0fa7a0043a91e93a9b8f793bcbbba9691e7fd83dca95c28ba26d21f0a"}, + {file = "fastjsonschema-2.20.0.tar.gz", hash = "sha256:3d48fc5300ee96f5d116f10fe6f28d938e6008f59a6a025c2649475b87f76a23"}, +] + +[package.extras] +devel = ["colorama", "json-spec", "jsonschema", "pylint", "pytest", "pytest-benchmark", "pytest-cache", "validictory"] + +[[package]] +name = "filelock" +version = "3.15.4" +description = "A platform independent file lock." +optional = false +python-versions = ">=3.8" +files = [ + {file = "filelock-3.15.4-py3-none-any.whl", hash = "sha256:6ca1fffae96225dab4c6eaf1c4f4f28cd2568d3ec2a44e15a08520504de468e7"}, + {file = "filelock-3.15.4.tar.gz", hash = "sha256:2207938cbc1844345cb01a5a95524dae30f0ce089eba5b00378295a17e3e90cb"}, +] + +[package.extras] +docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8.0.1)", "pytest (>=7.4.3)", "pytest-asyncio (>=0.21)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)", "virtualenv (>=20.26.2)"] +typing = ["typing-extensions (>=4.8)"] + +[[package]] +name = "flake8" +version = "7.1.1" +description = "the modular source code checker: pep8 pyflakes and co" +optional = false +python-versions = ">=3.8.1" +files = [ + {file = "flake8-7.1.1-py2.py3-none-any.whl", hash = "sha256:597477df7860daa5aa0fdd84bf5208a043ab96b8e96ab708770ae0364dd03213"}, + {file = "flake8-7.1.1.tar.gz", hash = "sha256:049d058491e228e03e67b390f311bbf88fce2dbaa8fa673e7aea87b7198b8d38"}, +] + +[package.dependencies] +mccabe = ">=0.7.0,<0.8.0" +pycodestyle = ">=2.12.0,<2.13.0" +pyflakes = ">=3.2.0,<3.3.0" + +[[package]] +name = "frozenlist" +version = "1.4.1" +description = "A list-like structure which implements collections.abc.MutableSequence" +optional = false +python-versions = ">=3.8" +files = [ + {file = "frozenlist-1.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f9aa1878d1083b276b0196f2dfbe00c9b7e752475ed3b682025ff20c1c1f51ac"}, + {file = "frozenlist-1.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:29acab3f66f0f24674b7dc4736477bcd4bc3ad4b896f5f45379a67bce8b96868"}, + {file = "frozenlist-1.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:74fb4bee6880b529a0c6560885fce4dc95936920f9f20f53d99a213f7bf66776"}, + {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:590344787a90ae57d62511dd7c736ed56b428f04cd8c161fcc5e7232c130c69a"}, + {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:068b63f23b17df8569b7fdca5517edef76171cf3897eb68beb01341131fbd2ad"}, + {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c849d495bf5154cd8da18a9eb15db127d4dba2968d88831aff6f0331ea9bd4c"}, + {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9750cc7fe1ae3b1611bb8cfc3f9ec11d532244235d75901fb6b8e42ce9229dfe"}, + {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9b2de4cf0cdd5bd2dee4c4f63a653c61d2408055ab77b151c1957f221cabf2a"}, + {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0633c8d5337cb5c77acbccc6357ac49a1770b8c487e5b3505c57b949b4b82e98"}, + {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:27657df69e8801be6c3638054e202a135c7f299267f1a55ed3a598934f6c0d75"}, + {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:f9a3ea26252bd92f570600098783d1371354d89d5f6b7dfd87359d669f2109b5"}, + {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:4f57dab5fe3407b6c0c1cc907ac98e8a189f9e418f3b6e54d65a718aaafe3950"}, + {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e02a0e11cf6597299b9f3bbd3f93d79217cb90cfd1411aec33848b13f5c656cc"}, + {file = "frozenlist-1.4.1-cp310-cp310-win32.whl", hash = "sha256:a828c57f00f729620a442881cc60e57cfcec6842ba38e1b19fd3e47ac0ff8dc1"}, + {file = "frozenlist-1.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:f56e2333dda1fe0f909e7cc59f021eba0d2307bc6f012a1ccf2beca6ba362439"}, + {file = "frozenlist-1.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a0cb6f11204443f27a1628b0e460f37fb30f624be6051d490fa7d7e26d4af3d0"}, + {file = "frozenlist-1.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b46c8ae3a8f1f41a0d2ef350c0b6e65822d80772fe46b653ab6b6274f61d4a49"}, + {file = "frozenlist-1.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fde5bd59ab5357e3853313127f4d3565fc7dad314a74d7b5d43c22c6a5ed2ced"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:722e1124aec435320ae01ee3ac7bec11a5d47f25d0ed6328f2273d287bc3abb0"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2471c201b70d58a0f0c1f91261542a03d9a5e088ed3dc6c160d614c01649c106"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c757a9dd70d72b076d6f68efdbb9bc943665ae954dad2801b874c8c69e185068"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f146e0911cb2f1da549fc58fc7bcd2b836a44b79ef871980d605ec392ff6b0d2"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f9c515e7914626b2a2e1e311794b4c35720a0be87af52b79ff8e1429fc25f19"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c302220494f5c1ebeb0912ea782bcd5e2f8308037b3c7553fad0e48ebad6ad82"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:442acde1e068288a4ba7acfe05f5f343e19fac87bfc96d89eb886b0363e977ec"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:1b280e6507ea8a4fa0c0a7150b4e526a8d113989e28eaaef946cc77ffd7efc0a"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:fe1a06da377e3a1062ae5fe0926e12b84eceb8a50b350ddca72dc85015873f74"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:db9e724bebd621d9beca794f2a4ff1d26eed5965b004a97f1f1685a173b869c2"}, + {file = "frozenlist-1.4.1-cp311-cp311-win32.whl", hash = "sha256:e774d53b1a477a67838a904131c4b0eef6b3d8a651f8b138b04f748fccfefe17"}, + {file = "frozenlist-1.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:fb3c2db03683b5767dedb5769b8a40ebb47d6f7f45b1b3e3b4b51ec8ad9d9825"}, + {file = "frozenlist-1.4.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1979bc0aeb89b33b588c51c54ab0161791149f2461ea7c7c946d95d5f93b56ae"}, + {file = "frozenlist-1.4.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cc7b01b3754ea68a62bd77ce6020afaffb44a590c2289089289363472d13aedb"}, + {file = "frozenlist-1.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c9c92be9fd329ac801cc420e08452b70e7aeab94ea4233a4804f0915c14eba9b"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c3894db91f5a489fc8fa6a9991820f368f0b3cbdb9cd8849547ccfab3392d86"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ba60bb19387e13597fb059f32cd4d59445d7b18b69a745b8f8e5db0346f33480"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8aefbba5f69d42246543407ed2461db31006b0f76c4e32dfd6f42215a2c41d09"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:780d3a35680ced9ce682fbcf4cb9c2bad3136eeff760ab33707b71db84664e3a"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9acbb16f06fe7f52f441bb6f413ebae6c37baa6ef9edd49cdd567216da8600cd"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:23b701e65c7b36e4bf15546a89279bd4d8675faabc287d06bbcfac7d3c33e1e6"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3e0153a805a98f5ada7e09826255ba99fb4f7524bb81bf6b47fb702666484ae1"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:dd9b1baec094d91bf36ec729445f7769d0d0cf6b64d04d86e45baf89e2b9059b"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:1a4471094e146b6790f61b98616ab8e44f72661879cc63fa1049d13ef711e71e"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5667ed53d68d91920defdf4035d1cdaa3c3121dc0b113255124bcfada1cfa1b8"}, + {file = "frozenlist-1.4.1-cp312-cp312-win32.whl", hash = "sha256:beee944ae828747fd7cb216a70f120767fc9f4f00bacae8543c14a6831673f89"}, + {file = "frozenlist-1.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:64536573d0a2cb6e625cf309984e2d873979709f2cf22839bf2d61790b448ad5"}, + {file = "frozenlist-1.4.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:20b51fa3f588ff2fe658663db52a41a4f7aa6c04f6201449c6c7c476bd255c0d"}, + {file = "frozenlist-1.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:410478a0c562d1a5bcc2f7ea448359fcb050ed48b3c6f6f4f18c313a9bdb1826"}, + {file = "frozenlist-1.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c6321c9efe29975232da3bd0af0ad216800a47e93d763ce64f291917a381b8eb"}, + {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48f6a4533887e189dae092f1cf981f2e3885175f7a0f33c91fb5b7b682b6bab6"}, + {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6eb73fa5426ea69ee0e012fb59cdc76a15b1283d6e32e4f8dc4482ec67d1194d"}, + {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fbeb989b5cc29e8daf7f976b421c220f1b8c731cbf22b9130d8815418ea45887"}, + {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:32453c1de775c889eb4e22f1197fe3bdfe457d16476ea407472b9442e6295f7a"}, + {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:693945278a31f2086d9bf3df0fe8254bbeaef1fe71e1351c3bd730aa7d31c41b"}, + {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:1d0ce09d36d53bbbe566fe296965b23b961764c0bcf3ce2fa45f463745c04701"}, + {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:3a670dc61eb0d0eb7080890c13de3066790f9049b47b0de04007090807c776b0"}, + {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:dca69045298ce5c11fd539682cff879cc1e664c245d1c64da929813e54241d11"}, + {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a06339f38e9ed3a64e4c4e43aec7f59084033647f908e4259d279a52d3757d09"}, + {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b7f2f9f912dca3934c1baec2e4585a674ef16fe00218d833856408c48d5beee7"}, + {file = "frozenlist-1.4.1-cp38-cp38-win32.whl", hash = "sha256:e7004be74cbb7d9f34553a5ce5fb08be14fb33bc86f332fb71cbe5216362a497"}, + {file = "frozenlist-1.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:5a7d70357e7cee13f470c7883a063aae5fe209a493c57d86eb7f5a6f910fae09"}, + {file = "frozenlist-1.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:bfa4a17e17ce9abf47a74ae02f32d014c5e9404b6d9ac7f729e01562bbee601e"}, + {file = "frozenlist-1.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b7e3ed87d4138356775346e6845cccbe66cd9e207f3cd11d2f0b9fd13681359d"}, + {file = "frozenlist-1.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c99169d4ff810155ca50b4da3b075cbde79752443117d89429595c2e8e37fed8"}, + {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edb678da49d9f72c9f6c609fbe41a5dfb9a9282f9e6a2253d5a91e0fc382d7c0"}, + {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6db4667b187a6742b33afbbaf05a7bc551ffcf1ced0000a571aedbb4aa42fc7b"}, + {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55fdc093b5a3cb41d420884cdaf37a1e74c3c37a31f46e66286d9145d2063bd0"}, + {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82e8211d69a4f4bc360ea22cd6555f8e61a1bd211d1d5d39d3d228b48c83a897"}, + {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89aa2c2eeb20957be2d950b85974b30a01a762f3308cd02bb15e1ad632e22dc7"}, + {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9d3e0c25a2350080e9319724dede4f31f43a6c9779be48021a7f4ebde8b2d742"}, + {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7268252af60904bf52c26173cbadc3a071cece75f873705419c8681f24d3edea"}, + {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:0c250a29735d4f15321007fb02865f0e6b6a41a6b88f1f523ca1596ab5f50bd5"}, + {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:96ec70beabbd3b10e8bfe52616a13561e58fe84c0101dd031dc78f250d5128b9"}, + {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:23b2d7679b73fe0e5a4560b672a39f98dfc6f60df63823b0a9970525325b95f6"}, + {file = "frozenlist-1.4.1-cp39-cp39-win32.whl", hash = "sha256:a7496bfe1da7fb1a4e1cc23bb67c58fab69311cc7d32b5a99c2007b4b2a0e932"}, + {file = "frozenlist-1.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:e6a20a581f9ce92d389a8c7d7c3dd47c81fd5d6e655c8dddf341e14aa48659d0"}, + {file = "frozenlist-1.4.1-py3-none-any.whl", hash = "sha256:04ced3e6a46b4cfffe20f9ae482818e34eba9b5fb0ce4056e4cc9b6e212d09b7"}, + {file = "frozenlist-1.4.1.tar.gz", hash = "sha256:c037a86e8513059a2613aaba4d817bb90b9d9b6b69aace3ce9c877e8c8ed402b"}, +] + +[[package]] +name = "fsspec" +version = "2024.6.1" +description = "File-system specification" +optional = false +python-versions = ">=3.8" +files = [ + {file = "fsspec-2024.6.1-py3-none-any.whl", hash = "sha256:3cb443f8bcd2efb31295a5b9fdb02aee81d8452c80d28f97a6d0959e6cee101e"}, + {file = "fsspec-2024.6.1.tar.gz", hash = "sha256:fad7d7e209dd4c1208e3bbfda706620e0da5142bebbd9c384afb95b07e798e49"}, +] + +[package.dependencies] +aiohttp = {version = "<4.0.0a0 || >4.0.0a0,<4.0.0a1 || >4.0.0a1", optional = true, markers = "extra == \"http\""} + +[package.extras] +abfs = ["adlfs"] +adl = ["adlfs"] +arrow = ["pyarrow (>=1)"] +dask = ["dask", "distributed"] +dev = ["pre-commit", "ruff"] +doc = ["numpydoc", "sphinx", "sphinx-design", "sphinx-rtd-theme", "yarl"] +dropbox = ["dropbox", "dropboxdrivefs", "requests"] +full = ["adlfs", "aiohttp (!=4.0.0a0,!=4.0.0a1)", "dask", "distributed", "dropbox", "dropboxdrivefs", "fusepy", "gcsfs", "libarchive-c", "ocifs", "panel", "paramiko", "pyarrow (>=1)", "pygit2", "requests", "s3fs", "smbprotocol", "tqdm"] +fuse = ["fusepy"] +gcs = ["gcsfs"] +git = ["pygit2"] +github = ["requests"] +gs = ["gcsfs"] +gui = ["panel"] +hdfs = ["pyarrow (>=1)"] +http = ["aiohttp (!=4.0.0a0,!=4.0.0a1)"] +libarchive = ["libarchive-c"] +oci = ["ocifs"] +s3 = ["s3fs"] +sftp = ["paramiko"] +smb = ["smbprotocol"] +ssh = ["paramiko"] +test = ["aiohttp (!=4.0.0a0,!=4.0.0a1)", "numpy", "pytest", "pytest-asyncio (!=0.22.0)", "pytest-benchmark", "pytest-cov", "pytest-mock", "pytest-recording", "pytest-rerunfailures", "requests"] +test-downstream = ["aiobotocore (>=2.5.4,<3.0.0)", "dask-expr", "dask[dataframe,test]", "moto[server] (>4,<5)", "pytest-timeout", "xarray"] +test-full = ["adlfs", "aiohttp (!=4.0.0a0,!=4.0.0a1)", "cloudpickle", "dask", "distributed", "dropbox", "dropboxdrivefs", "fastparquet", "fusepy", "gcsfs", "jinja2", "kerchunk", "libarchive-c", "lz4", "notebook", "numpy", "ocifs", "pandas", "panel", "paramiko", "pyarrow", "pyarrow (>=1)", "pyftpdlib", "pygit2", "pytest", "pytest-asyncio (!=0.22.0)", "pytest-benchmark", "pytest-cov", "pytest-mock", "pytest-recording", "pytest-rerunfailures", "python-snappy", "requests", "smbprotocol", "tqdm", "urllib3", "zarr", "zstandard"] +tqdm = ["tqdm"] + +[[package]] +name = "fvcore" +version = "0.1.5.post20221221" +description = "Collection of common code shared among different research projects in FAIR computer vision team" +optional = false +python-versions = ">=3.6" +files = [ + {file = "fvcore-0.1.5.post20221221.tar.gz", hash = "sha256:f2fb0bb90572ae651c11c78e20493ed19b2240550a7e4bbb2d6de87bdd037860"}, +] + +[package.dependencies] +iopath = ">=0.1.7" +numpy = "*" +Pillow = "*" +pyyaml = ">=5.1" +tabulate = "*" +termcolor = ">=1.1" +tqdm = "*" +yacs = ">=0.1.6" + +[package.extras] +all = ["shapely"] + +[[package]] +name = "gitdb" +version = "4.0.11" +description = "Git Object Database" +optional = false +python-versions = ">=3.7" +files = [ + {file = "gitdb-4.0.11-py3-none-any.whl", hash = "sha256:81a3407ddd2ee8df444cbacea00e2d038e40150acfa3001696fe0dcf1d3adfa4"}, + {file = "gitdb-4.0.11.tar.gz", hash = "sha256:bf5421126136d6d0af55bc1e7c1af1c397a34f5b7bd79e776cd3e89785c2b04b"}, +] + +[package.dependencies] +smmap = ">=3.0.1,<6" + +[[package]] +name = "gitpython" +version = "3.1.43" +description = "GitPython is a Python library used to interact with Git repositories" +optional = false +python-versions = ">=3.7" +files = [ + {file = "GitPython-3.1.43-py3-none-any.whl", hash = "sha256:eec7ec56b92aad751f9912a73404bc02ba212a23adb2c7098ee668417051a1ff"}, + {file = "GitPython-3.1.43.tar.gz", hash = "sha256:35f314a9f878467f5453cc1fee295c3e18e52f1b99f10f6cf5b1682e968a9e7c"}, +] + +[package.dependencies] +gitdb = ">=4.0.1,<5" + +[package.extras] +doc = ["sphinx (==4.3.2)", "sphinx-autodoc-typehints", "sphinx-rtd-theme", "sphinxcontrib-applehelp (>=1.0.2,<=1.0.4)", "sphinxcontrib-devhelp (==1.0.2)", "sphinxcontrib-htmlhelp (>=2.0.0,<=2.0.1)", "sphinxcontrib-qthelp (==1.0.3)", "sphinxcontrib-serializinghtml (==1.1.5)"] +test = ["coverage[toml]", "ddt (>=1.1.1,!=1.4.3)", "mock", "mypy", "pre-commit", "pytest (>=7.3.1)", "pytest-cov", "pytest-instafail", "pytest-mock", "pytest-sugar", "typing-extensions"] + +[[package]] +name = "html5lib" +version = "1.1" +description = "HTML parser based on the WHATWG HTML specification" +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.24.5" +description = "Client library to download and publish models, datasets and other repos on the huggingface.co hub" +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "huggingface_hub-0.24.5-py3-none-any.whl", hash = "sha256:d93fb63b1f1a919a22ce91a14518974e81fc4610bf344dfe7572343ce8d3aced"}, + {file = "huggingface_hub-0.24.5.tar.gz", hash = "sha256:7b45d6744dd53ce9cbf9880957de00e9d10a9ae837f1c9b7255fc8fa4e8264f3"}, +] + +[package.dependencies] +filelock = "*" +fsspec = ">=2023.5.0" +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", "aiohttp", "fastapi", "gradio", "jedi", "minijinja (>=1.0)", "mypy (==1.5.1)", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "ruff (>=0.5.0)", "soundfile", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"] +cli = ["InquirerPy (==0.3.4)"] +dev = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "fastapi", "gradio", "jedi", "minijinja (>=1.0)", "mypy (==1.5.1)", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "ruff (>=0.5.0)", "soundfile", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"] +fastai = ["fastai (>=2.4)", "fastcore (>=1.3.27)", "toml"] +hf-transfer = ["hf-transfer (>=0.1.4)"] +inference = ["aiohttp", "minijinja (>=1.0)"] +quality = ["mypy (==1.5.1)", "ruff (>=0.5.0)"] +tensorflow = ["graphviz", "pydot", "tensorflow"] +tensorflow-testing = ["keras (<3.0)", "tensorflow"] +testing = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "fastapi", "gradio", "jedi", "minijinja (>=1.0)", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "soundfile", "urllib3 (<2.0)"] +torch = ["safetensors[torch]", "torch"] +typing = ["types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)"] + +[[package]] +name = "hydra-core" +version = "1.3.2" +description = "A framework for elegantly configuring complex applications" +optional = false +python-versions = "*" +files = [ + {file = "hydra-core-1.3.2.tar.gz", hash = "sha256:8a878ed67216997c3e9d88a8e72e7b4767e81af37afb4ea3334b269a4390a824"}, + {file = "hydra_core-1.3.2-py3-none-any.whl", hash = "sha256:fa0238a9e31df3373b35b0bfb672c34cc92718d21f81311d8996a16de1141d8b"}, +] + +[package.dependencies] +antlr4-python3-runtime = "==4.9.*" +omegaconf = ">=2.2,<2.4" +packaging = "*" + +[[package]] +name = "hydra-submitit-launcher" +version = "1.2.0" +description = "Submitit Launcher for Hydra apps" +optional = false +python-versions = "*" +files = [ + {file = "hydra-submitit-launcher-1.2.0.tar.gz", hash = "sha256:e14c8eb46d020fac60ba25f82bcc368dc55851d2683dc95c88631ffcf15e4a34"}, + {file = "hydra_submitit_launcher-1.2.0-py3-none-any.whl", hash = "sha256:51ce468fbc91211c3a46677eefde94bbb9f721c9545af0be6dd0a95658515613"}, +] + +[package.dependencies] +hydra-core = ">=1.1.0.dev7" +submitit = ">=1.3.3" + +[[package]] +name = "hydra-zen" +version = "0.13.0" +description = "Configurable, reproducible, and scalable workflows in Python, via Hydra" +optional = false +python-versions = ">=3.8" +files = [ + {file = "hydra_zen-0.13.0-py3-none-any.whl", hash = "sha256:6050b62be96d2a47b2abf0e9c0ebcce1e9a4e259e173870338ab049b833f26cf"}, + {file = "hydra_zen-0.13.0.tar.gz", hash = "sha256:1b53d74aa1f0baa04fafdac6aba7a94ae40929e7b0a5a5081d8740f74322052d"}, +] + +[package.dependencies] +hydra-core = ">=1.2.0" +omegaconf = ">=2.2.1" +typing-extensions = ">=4.1.0,<4.6.0 || >4.6.0" + +[package.extras] +beartype = ["beartype (>=0.8.0)"] +pydantic = ["pydantic (>=1.10.14,<2.0.0)"] +test = ["hypothesis (>=6.28.0)", "pytest (>=3.8)", "pytest-trio (>=0.8.0)"] + +[[package]] +name = "identify" +version = "2.6.0" +description = "File identification library for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "identify-2.6.0-py2.py3-none-any.whl", hash = "sha256:e79ae4406387a9d300332b5fd366d8994f1525e8414984e1a59e058b2eda2dd0"}, + {file = "identify-2.6.0.tar.gz", hash = "sha256:cb171c685bdc31bcc4c1734698736a7d5b6c8bf2e0c15117f4d469c8640ae5cf"}, +] + +[package.extras] +license = ["ukkonen"] + +[[package]] +name = "idna" +version = "3.7" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.5" +files = [ + {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"}, + {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"}, +] + +[[package]] +name = "importlib-metadata" +version = "8.2.0" +description = "Read metadata from Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "importlib_metadata-8.2.0-py3-none-any.whl", hash = "sha256:11901fa0c2f97919b288679932bb64febaeacf289d18ac84dd68cb2e74213369"}, + {file = "importlib_metadata-8.2.0.tar.gz", hash = "sha256:72e8d4399996132204f9a16dcc751af254a48f8d1b20b9ff0f98d4a8f901e73d"}, +] + +[package.dependencies] +zipp = ">=0.5" + +[package.extras] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +perf = ["ipython"] +test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"] + +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + +[[package]] +name = "intel-openmp" +version = "2021.4.0" +description = "Intel OpenMP* Runtime Library" +optional = false +python-versions = "*" +files = [ + {file = "intel_openmp-2021.4.0-py2.py3-none-macosx_10_15_x86_64.macosx_11_0_x86_64.whl", hash = "sha256:41c01e266a7fdb631a7609191709322da2bbf24b252ba763f125dd651bcc7675"}, + {file = "intel_openmp-2021.4.0-py2.py3-none-manylinux1_i686.whl", hash = "sha256:3b921236a38384e2016f0f3d65af6732cf2c12918087128a9163225451e776f2"}, + {file = "intel_openmp-2021.4.0-py2.py3-none-manylinux1_x86_64.whl", hash = "sha256:e2240ab8d01472fed04f3544a878cda5da16c26232b7ea1b59132dbfb48b186e"}, + {file = "intel_openmp-2021.4.0-py2.py3-none-win32.whl", hash = "sha256:6e863d8fd3d7e8ef389d52cf97a50fe2afe1a19247e8c0d168ce021546f96fc9"}, + {file = "intel_openmp-2021.4.0-py2.py3-none-win_amd64.whl", hash = "sha256:eef4c8bcc8acefd7f5cd3b9384dbf73d59e2c99fc56545712ded913f43c4a94f"}, +] + +[[package]] +name = "iopath" +version = "0.1.10" +description = "A library for providing I/O abstraction." +optional = false +python-versions = ">=3.6" +files = [ + {file = "iopath-0.1.10.tar.gz", hash = "sha256:3311c16a4d9137223e20f141655759933e1eda24f8bff166af834af3c645ef01"}, +] + +[package.dependencies] +portalocker = "*" +tqdm = "*" +typing_extensions = "*" + +[package.extras] +aws = ["boto3"] + +[[package]] +name = "ipykernel" +version = "6.29.5" +description = "IPython Kernel for Jupyter" +optional = false +python-versions = ">=3.8" +files = [ + {file = "ipykernel-6.29.5-py3-none-any.whl", hash = "sha256:afdb66ba5aa354b09b91379bac28ae4afebbb30e8b39510c9690afb7a10421b5"}, + {file = "ipykernel-6.29.5.tar.gz", hash = "sha256:f093a22c4a40f8828f8e330a9c297cb93dcab13bd9678ded6de8e5cf81c56215"}, +] + +[package.dependencies] +appnope = {version = "*", markers = "platform_system == \"Darwin\""} +comm = ">=0.1.1" +debugpy = ">=1.6.5" +ipython = ">=7.23.1" +jupyter-client = ">=6.1.12" +jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0" +matplotlib-inline = ">=0.1" +nest-asyncio = "*" +packaging = "*" +psutil = "*" +pyzmq = ">=24" +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 (>=0.23.5)", "pytest-cov", "pytest-timeout"] + +[[package]] +name = "ipython" +version = "8.18.0" +description = "IPython: Productive Interactive Computing" +optional = false +python-versions = ">=3.9" +files = [ + {file = "ipython-8.18.0-py3-none-any.whl", hash = "sha256:d538a7a98ad9b7e018926447a5f35856113a85d08fd68a165d7871ab5175f6e0"}, + {file = "ipython-8.18.0.tar.gz", hash = "sha256:4feb61210160f75e229ce932dbf8b719bff37af123c0b985fd038b14233daa16"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +decorator = "*" +exceptiongroup = {version = "*", markers = "python_version < \"3.11\""} +jedi = ">=0.16" +matplotlib-inline = "*" +pexpect = {version = ">4.3", markers = "sys_platform != \"win32\""} +prompt-toolkit = ">=3.0.30,<3.0.37 || >3.0.37,<3.1.0" +pygments = ">=2.4.0" +stack-data = "*" +traitlets = ">=5" +typing-extensions = {version = "*", markers = "python_version < \"3.10\""} + +[package.extras] +all = ["black", "curio", "docrepr", "exceptiongroup", "ipykernel", "ipyparallel", "ipywidgets", "matplotlib", "matplotlib (!=3.2.0)", "nbconvert", "nbformat", "notebook", "numpy (>=1.22)", "pandas", "pickleshare", "pytest (<7)", "pytest (<7.1)", "pytest-asyncio (<0.22)", "qtconsole", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "stack-data", "testpath", "trio", "typing-extensions"] +black = ["black"] +doc = ["docrepr", "exceptiongroup", "ipykernel", "matplotlib", "pickleshare", "pytest (<7)", "pytest (<7.1)", "pytest-asyncio (<0.22)", "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 = ["pickleshare", "pytest (<7.1)", "pytest-asyncio (<0.22)", "testpath"] +test-extra = ["curio", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.22)", "pandas", "pickleshare", "pytest (<7.1)", "pytest-asyncio (<0.22)", "testpath", "trio"] + +[[package]] +name = "isort" +version = "5.13.2" +description = "A Python utility / library to sort Python imports." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, + {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, +] + +[package.extras] +colors = ["colorama (>=0.4.6)"] + +[[package]] +name = "jedi" +version = "0.19.1" +description = "An autocompletion tool for Python that can be used for text editors." +optional = false +python-versions = ">=3.6" +files = [ + {file = "jedi-0.19.1-py2.py3-none-any.whl", hash = "sha256:e983c654fe5c02867aef4cdfce5a2fbb4a50adc0af145f70504238f18ef5e7e0"}, + {file = "jedi-0.19.1.tar.gz", hash = "sha256:cf0496f3651bc65d7174ac1b7d043eff454892c708a87d1b683e57b569927ffd"}, +] + +[package.dependencies] +parso = ">=0.8.3,<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 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"] +testing = ["Django", "attrs", "colorama", "docopt", "pytest (<7.0.0)"] + +[[package]] +name = "jinja2" +version = "3.1.4" +description = "A very fast and expressive template engine." +optional = false +python-versions = ">=3.7" +files = [ + {file = "jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"}, + {file = "jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369"}, +] + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "jsonlines" +version = "4.0.0" +description = "Library with helpers for the jsonlines file format" +optional = false +python-versions = ">=3.8" +files = [ + {file = "jsonlines-4.0.0-py3-none-any.whl", hash = "sha256:185b334ff2ca5a91362993f42e83588a360cf95ce4b71a73548502bda52a7c55"}, + {file = "jsonlines-4.0.0.tar.gz", hash = "sha256:0c6d2c09117550c089995247f605ae4cf77dd1533041d366351f6f298822ea74"}, +] + +[package.dependencies] +attrs = ">=19.2.0" + +[[package]] +name = "jsonschema" +version = "4.23.0" +description = "An implementation of JSON Schema validation for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "jsonschema-4.23.0-py3-none-any.whl", hash = "sha256:fbadb6f8b144a8f8cf9f0b89ba94501d143e50411a1278633f56a7acf7fd5566"}, + {file = "jsonschema-4.23.0.tar.gz", hash = "sha256:d71497fef26351a33265337fa77ffeb82423f3ea21283cd9467bb03999266bc4"}, +] + +[package.dependencies] +attrs = ">=22.2.0" +jsonschema-specifications = ">=2023.03.6" +referencing = ">=0.28.4" +rpds-py = ">=0.7.1" + +[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 (>=24.6.0)"] + +[[package]] +name = "jsonschema-specifications" +version = "2023.12.1" +description = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry" +optional = false +python-versions = ">=3.8" +files = [ + {file = "jsonschema_specifications-2023.12.1-py3-none-any.whl", hash = "sha256:87e4fdf3a94858b8a2ba2778d9ba57d8a9cafca7c7489c46ba0d30a8bc6a9c3c"}, + {file = "jsonschema_specifications-2023.12.1.tar.gz", hash = "sha256:48a76787b3e70f5ed53f1160d2b81f586e4ca6d1548c5de7085d1682674764cc"}, +] + +[package.dependencies] +referencing = ">=0.31.0" + +[[package]] +name = "jupyter-client" +version = "8.6.2" +description = "Jupyter protocol implementation and client libraries" +optional = false +python-versions = ">=3.8" +files = [ + {file = "jupyter_client-8.6.2-py3-none-any.whl", hash = "sha256:50cbc5c66fd1b8f65ecb66bc490ab73217993632809b6e505687de18e9dea39f"}, + {file = "jupyter_client-8.6.2.tar.gz", hash = "sha256:2bda14d55ee5ba58552a8c53ae43d215ad9868853489213f37da060ced54d8df"}, +] + +[package.dependencies] +importlib-metadata = {version = ">=4.8.3", markers = "python_version < \"3.10\""} +jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0" +python-dateutil = ">=2.8.2" +pyzmq = ">=23.0" +tornado = ">=6.2" +traitlets = ">=5.3" + +[package.extras] +docs = ["ipykernel", "myst-parser", "pydata-sphinx-theme", "sphinx (>=4)", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-spelling"] +test = ["coverage", "ipykernel (>=6.14)", "mypy", "paramiko", "pre-commit", "pytest (<8.2.0)", "pytest-cov", "pytest-jupyter[client] (>=0.4.1)", "pytest-timeout"] + +[[package]] +name = "jupyter-core" +version = "5.7.2" +description = "Jupyter core package. A base package on which Jupyter projects rely." +optional = false +python-versions = ">=3.8" +files = [ + {file = "jupyter_core-5.7.2-py3-none-any.whl", hash = "sha256:4f7315d2f6b4bcf2e3e7cb6e46772eba760ae459cd1f59d29eb57b0a01bd7409"}, + {file = "jupyter_core-5.7.2.tar.gz", hash = "sha256:aa5f8d32bbf6b431ac830496da7392035d6f61b4f54872f15c4bd2a9c3f536d9"}, +] + +[package.dependencies] +platformdirs = ">=2.5" +pywin32 = {version = ">=300", markers = "sys_platform == \"win32\" and platform_python_implementation != \"PyPy\""} +traitlets = ">=5.3" + +[package.extras] +docs = ["myst-parser", "pydata-sphinx-theme", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-spelling", "traitlets"] +test = ["ipykernel", "pre-commit", "pytest (<8)", "pytest-cov", "pytest-timeout"] + +[[package]] +name = "jupytext" +version = "1.16.4" +description = "Jupyter notebooks as Markdown documents, Julia, Python or R scripts" +optional = false +python-versions = ">=3.8" +files = [ + {file = "jupytext-1.16.4-py3-none-any.whl", hash = "sha256:76989d2690e65667ea6fb411d8056abe7cd0437c07bd774660b83d62acf9490a"}, + {file = "jupytext-1.16.4.tar.gz", hash = "sha256:28e33f46f2ce7a41fb9d677a4a2c95327285579b64ca104437c4b9eb1e4174e9"}, +] + +[package.dependencies] +markdown-it-py = ">=1.0" +mdit-py-plugins = "*" +nbformat = "*" +packaging = "*" +pyyaml = "*" +tomli = {version = "*", markers = "python_version < \"3.11\""} + +[package.extras] +dev = ["autopep8", "black", "flake8", "gitpython", "ipykernel", "isort", "jupyter-fs (>=1.0)", "jupyter-server (!=2.11)", "nbconvert", "pre-commit", "pytest", "pytest-cov (>=2.6.1)", "pytest-randomly", "pytest-xdist", "sphinx-gallery (<0.8)"] +docs = ["myst-parser", "sphinx", "sphinx-copybutton", "sphinx-rtd-theme"] +test = ["pytest", "pytest-randomly", "pytest-xdist"] +test-cov = ["ipykernel", "jupyter-server (!=2.11)", "nbconvert", "pytest", "pytest-cov (>=2.6.1)", "pytest-randomly", "pytest-xdist"] +test-external = ["autopep8", "black", "flake8", "gitpython", "ipykernel", "isort", "jupyter-fs (>=1.0)", "jupyter-server (!=2.11)", "nbconvert", "pre-commit", "pytest", "pytest-randomly", "pytest-xdist", "sphinx-gallery (<0.8)"] +test-functional = ["pytest", "pytest-randomly", "pytest-xdist"] +test-integration = ["ipykernel", "jupyter-server (!=2.11)", "nbconvert", "pytest", "pytest-randomly", "pytest-xdist"] +test-ui = ["calysto-bash"] + +[[package]] +name = "license-expression" +version = "30.3.0" +description = "license-expression is a comprehensive utility library to parse, compare, simplify and normalize license expressions (such as SPDX license expressions) using boolean logic." +optional = false +python-versions = ">=3.8" +files = [ + {file = "license-expression-30.3.0.tar.gz", hash = "sha256:1295406f736b4f395ff069aec1cebfad53c0fcb3cf57df0f5ec58fc7b905aea5"}, + {file = "license_expression-30.3.0-py3-none-any.whl", hash = "sha256:ae0ba9a829d6909c785dc2f0131f13d10d68318e4a5f28af5ef152d6b52f9b41"}, +] + +[package.dependencies] +"boolean.py" = ">=4.0" + +[package.extras] +docs = ["Sphinx (>=5.0.2)", "doc8 (>=0.11.2)", "sphinx-autobuild", "sphinx-copybutton", "sphinx-reredirects (>=0.1.2)", "sphinx-rtd-dark-mode (>=1.3.0)", "sphinx-rtd-theme (>=1.0.0)", "sphinxcontrib-apidoc (>=0.4.0)"] +testing = ["black", "isort", "pytest (>=6,!=7.0.0)", "pytest-xdist (>=2)", "twine"] + +[[package]] +name = "lightning" +version = "2.4.0" +description = "The Deep Learning framework to train, deploy, and ship AI products Lightning fast." +optional = false +python-versions = ">=3.9" +files = [ + {file = "lightning-2.4.0-py3-none-any.whl", hash = "sha256:560163af9711cf59055c448232c473150a299089efce0d2be3cc3288082d8768"}, + {file = "lightning-2.4.0.tar.gz", hash = "sha256:9156604cc56e4b2b603f34fa7f0fe5107375c8e6d85e74544b319a15faa9ed0e"}, +] + +[package.dependencies] +fsspec = {version = ">=2022.5.0,<2026.0", extras = ["http"]} +lightning-utilities = ">=0.10.0,<2.0" +packaging = ">=20.0,<25.0" +pytorch-lightning = "*" +PyYAML = ">=5.4,<8.0" +torch = ">=2.1.0,<4.0" +torchmetrics = ">=0.7.0,<3.0" +tqdm = ">=4.57.0,<6.0" +typing-extensions = ">=4.4.0,<6.0" + +[package.extras] +all = ["bitsandbytes (>=0.42.0,<1.0)", "deepspeed (>=0.8.2,<=0.9.3)", "hydra-core (>=1.2.0,<2.0)", "ipython[all] (<9.0)", "jsonargparse[signatures] (>=4.27.7,<5.0)", "lightning-utilities (>=0.8.0,<1.0)", "matplotlib (>3.1,<4.0)", "omegaconf (>=2.2.3,<3.0)", "requests (<3.0)", "rich (>=12.3.0,<14.0)", "tensorboardX (>=2.2,<3.0)", "torchmetrics (>=0.10.0,<2.0)", "torchvision (>=0.16.0,<1.0)"] +data = ["litdata (>=0.2.0rc,<1.0)"] +dev = ["bitsandbytes (>=0.42.0,<1.0)", "click (==8.1.7)", "cloudpickle (>=1.3,<3.0)", "coverage (==7.3.1)", "deepspeed (>=0.8.2,<=0.9.3)", "fastapi", "hydra-core (>=1.2.0,<2.0)", "ipython[all] (<9.0)", "jsonargparse[signatures] (>=4.27.7,<5.0)", "lightning-utilities (>=0.8.0,<1.0)", "matplotlib (>3.1,<4.0)", "numpy (>=1.17.2,<2.0)", "omegaconf (>=2.2.3,<3.0)", "onnx (>=1.12.0,<2.0)", "onnxruntime (>=1.12.0,<2.0)", "pandas (>1.0,<3.0)", "psutil (<6.0)", "pytest (==7.4.0)", "pytest-cov (==4.1.0)", "pytest-random-order (==1.1.0)", "pytest-rerunfailures (==12.0)", "pytest-timeout (==2.1.0)", "requests (<3.0)", "rich (>=12.3.0,<14.0)", "scikit-learn (>0.22.1,<2.0)", "tensorboard (>=2.9.1,<3.0)", "tensorboardX (>=2.2,<3.0)", "torchmetrics (>=0.10.0,<2.0)", "torchmetrics (>=0.7.0,<2.0)", "torchvision (>=0.16.0,<1.0)", "uvicorn"] +examples = ["ipython[all] (<9.0)", "lightning-utilities (>=0.8.0,<1.0)", "requests (<3.0)", "torchmetrics (>=0.10.0,<2.0)", "torchvision (>=0.16.0,<1.0)"] +extra = ["bitsandbytes (>=0.42.0,<1.0)", "hydra-core (>=1.2.0,<2.0)", "jsonargparse[signatures] (>=4.27.7,<5.0)", "matplotlib (>3.1,<4.0)", "omegaconf (>=2.2.3,<3.0)", "rich (>=12.3.0,<14.0)", "tensorboardX (>=2.2,<3.0)"] +fabric-all = ["bitsandbytes (>=0.42.0,<1.0)", "deepspeed (>=0.8.2,<=0.9.3)", "lightning-utilities (>=0.8.0,<1.0)", "torchmetrics (>=0.10.0,<2.0)", "torchvision (>=0.16.0,<1.0)"] +fabric-dev = ["bitsandbytes (>=0.42.0,<1.0)", "click (==8.1.7)", "coverage (==7.3.1)", "deepspeed (>=0.8.2,<=0.9.3)", "lightning-utilities (>=0.8.0,<1.0)", "numpy (>=1.17.2,<2.0)", "pytest (==7.4.0)", "pytest-cov (==4.1.0)", "pytest-random-order (==1.1.0)", "pytest-rerunfailures (==12.0)", "pytest-timeout (==2.1.0)", "tensorboardX (>=2.2,<3.0)", "torchmetrics (>=0.10.0,<2.0)", "torchmetrics (>=0.7.0,<2.0)", "torchvision (>=0.16.0,<1.0)"] +fabric-examples = ["lightning-utilities (>=0.8.0,<1.0)", "torchmetrics (>=0.10.0,<2.0)", "torchvision (>=0.16.0,<1.0)"] +fabric-strategies = ["bitsandbytes (>=0.42.0,<1.0)", "deepspeed (>=0.8.2,<=0.9.3)"] +fabric-test = ["click (==8.1.7)", "coverage (==7.3.1)", "numpy (>=1.17.2,<2.0)", "pytest (==7.4.0)", "pytest-cov (==4.1.0)", "pytest-random-order (==1.1.0)", "pytest-rerunfailures (==12.0)", "pytest-timeout (==2.1.0)", "tensorboardX (>=2.2,<3.0)", "torchmetrics (>=0.7.0,<2.0)"] +pytorch-all = ["bitsandbytes (>=0.42.0,<1.0)", "deepspeed (>=0.8.2,<=0.9.3)", "hydra-core (>=1.2.0,<2.0)", "ipython[all] (<9.0)", "jsonargparse[signatures] (>=4.27.7,<5.0)", "lightning-utilities (>=0.8.0,<1.0)", "matplotlib (>3.1,<4.0)", "omegaconf (>=2.2.3,<3.0)", "requests (<3.0)", "rich (>=12.3.0,<14.0)", "tensorboardX (>=2.2,<3.0)", "torchmetrics (>=0.10.0,<2.0)", "torchvision (>=0.16.0,<1.0)"] +pytorch-dev = ["bitsandbytes (>=0.42.0,<1.0)", "cloudpickle (>=1.3,<3.0)", "coverage (==7.3.1)", "deepspeed (>=0.8.2,<=0.9.3)", "fastapi", "hydra-core (>=1.2.0,<2.0)", "ipython[all] (<9.0)", "jsonargparse[signatures] (>=4.27.7,<5.0)", "lightning-utilities (>=0.8.0,<1.0)", "matplotlib (>3.1,<4.0)", "numpy (>=1.17.2,<2.0)", "omegaconf (>=2.2.3,<3.0)", "onnx (>=1.12.0,<2.0)", "onnxruntime (>=1.12.0,<2.0)", "pandas (>1.0,<3.0)", "psutil (<6.0)", "pytest (==7.4.0)", "pytest-cov (==4.1.0)", "pytest-random-order (==1.1.0)", "pytest-rerunfailures (==12.0)", "pytest-timeout (==2.1.0)", "requests (<3.0)", "rich (>=12.3.0,<14.0)", "scikit-learn (>0.22.1,<2.0)", "tensorboard (>=2.9.1,<3.0)", "tensorboardX (>=2.2,<3.0)", "torchmetrics (>=0.10.0,<2.0)", "torchvision (>=0.16.0,<1.0)", "uvicorn"] +pytorch-examples = ["ipython[all] (<9.0)", "lightning-utilities (>=0.8.0,<1.0)", "requests (<3.0)", "torchmetrics (>=0.10.0,<2.0)", "torchvision (>=0.16.0,<1.0)"] +pytorch-extra = ["bitsandbytes (>=0.42.0,<1.0)", "hydra-core (>=1.2.0,<2.0)", "jsonargparse[signatures] (>=4.27.7,<5.0)", "matplotlib (>3.1,<4.0)", "omegaconf (>=2.2.3,<3.0)", "rich (>=12.3.0,<14.0)", "tensorboardX (>=2.2,<3.0)"] +pytorch-strategies = ["deepspeed (>=0.8.2,<=0.9.3)"] +pytorch-test = ["cloudpickle (>=1.3,<3.0)", "coverage (==7.3.1)", "fastapi", "numpy (>=1.17.2,<2.0)", "onnx (>=1.12.0,<2.0)", "onnxruntime (>=1.12.0,<2.0)", "pandas (>1.0,<3.0)", "psutil (<6.0)", "pytest (==7.4.0)", "pytest-cov (==4.1.0)", "pytest-random-order (==1.1.0)", "pytest-rerunfailures (==12.0)", "pytest-timeout (==2.1.0)", "scikit-learn (>0.22.1,<2.0)", "tensorboard (>=2.9.1,<3.0)", "uvicorn"] +strategies = ["bitsandbytes (>=0.42.0,<1.0)", "deepspeed (>=0.8.2,<=0.9.3)"] +test = ["click (==8.1.7)", "cloudpickle (>=1.3,<3.0)", "coverage (==7.3.1)", "fastapi", "numpy (>=1.17.2,<2.0)", "onnx (>=1.12.0,<2.0)", "onnxruntime (>=1.12.0,<2.0)", "pandas (>1.0,<3.0)", "psutil (<6.0)", "pytest (==7.4.0)", "pytest-cov (==4.1.0)", "pytest-random-order (==1.1.0)", "pytest-rerunfailures (==12.0)", "pytest-timeout (==2.1.0)", "scikit-learn (>0.22.1,<2.0)", "tensorboard (>=2.9.1,<3.0)", "tensorboardX (>=2.2,<3.0)", "torchmetrics (>=0.7.0,<2.0)", "uvicorn"] + +[[package]] +name = "lightning-utilities" +version = "0.11.6" +description = "Lightning toolbox for across the our ecosystem." +optional = false +python-versions = ">=3.8" +files = [ + {file = "lightning_utilities-0.11.6-py3-none-any.whl", hash = "sha256:ecd9953c316cbaf56ad820fbe7bd062187b9973c4a23d47b076cd59dc080a310"}, + {file = "lightning_utilities-0.11.6.tar.gz", hash = "sha256:79fc27ef8ec8b8d55a537920f2c7610270c0c9e037fa6efc78f1aa34ec8cdf04"}, +] + +[package.dependencies] +packaging = ">=17.1" +setuptools = "*" +typing-extensions = "*" + +[package.extras] +cli = ["fire"] +docs = ["requests (>=2.0.0)"] +typing = ["mypy (>=1.0.0)", "types-setuptools"] + +[[package]] +name = "markdown-it-py" +version = "3.0.0" +description = "Python port of markdown-it. Markdown parsing, done right!" +optional = false +python-versions = ">=3.8" +files = [ + {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, + {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, +] + +[package.dependencies] +mdurl = ">=0.1,<1.0" + +[package.extras] +benchmarking = ["psutil", "pytest", "pytest-benchmark"] +code-style = ["pre-commit (>=3.0,<4.0)"] +compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"] +linkify = ["linkify-it-py (>=1,<3)"] +plugins = ["mdit-py-plugins"] +profiling = ["gprof2dot"] +rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] +testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] + +[[package]] +name = "markupsafe" +version = "2.1.5" +description = "Safely add untrusted strings to HTML/XML markup." +optional = false +python-versions = ">=3.7" +files = [ + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, + {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, +] + +[[package]] +name = "matplotlib-inline" +version = "0.1.7" +description = "Inline Matplotlib backend for Jupyter" +optional = false +python-versions = ">=3.8" +files = [ + {file = "matplotlib_inline-0.1.7-py3-none-any.whl", hash = "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca"}, + {file = "matplotlib_inline-0.1.7.tar.gz", hash = "sha256:8423b23ec666be3d16e16b60bdd8ac4e86e840ebd1dd11a30b9f117f2fa0ab90"}, +] + +[package.dependencies] +traitlets = "*" + +[[package]] +name = "mccabe" +version = "0.7.0" +description = "McCabe checker, plugin for flake8" +optional = false +python-versions = ">=3.6" +files = [ + {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, + {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, +] + +[[package]] +name = "mdit-py-plugins" +version = "0.4.1" +description = "Collection of plugins for markdown-it-py" +optional = false +python-versions = ">=3.8" +files = [ + {file = "mdit_py_plugins-0.4.1-py3-none-any.whl", hash = "sha256:1020dfe4e6bfc2c79fb49ae4e3f5b297f5ccd20f010187acc52af2921e27dc6a"}, + {file = "mdit_py_plugins-0.4.1.tar.gz", hash = "sha256:834b8ac23d1cd60cec703646ffd22ae97b7955a6d596eb1d304be1e251ae499c"}, +] + +[package.dependencies] +markdown-it-py = ">=1.0.0,<4.0.0" + +[package.extras] +code-style = ["pre-commit"] +rtd = ["myst-parser", "sphinx-book-theme"] +testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] + +[[package]] +name = "mdurl" +version = "0.1.2" +description = "Markdown URL utilities" +optional = false +python-versions = ">=3.7" +files = [ + {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, + {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, +] + +[[package]] +name = "mkl" +version = "2021.4.0" +description = "Intel® oneAPI Math Kernel Library" +optional = false +python-versions = "*" +files = [ + {file = "mkl-2021.4.0-py2.py3-none-macosx_10_15_x86_64.macosx_11_0_x86_64.whl", hash = "sha256:67460f5cd7e30e405b54d70d1ed3ca78118370b65f7327d495e9c8847705e2fb"}, + {file = "mkl-2021.4.0-py2.py3-none-manylinux1_i686.whl", hash = "sha256:636d07d90e68ccc9630c654d47ce9fdeb036bb46e2b193b3a9ac8cfea683cce5"}, + {file = "mkl-2021.4.0-py2.py3-none-manylinux1_x86_64.whl", hash = "sha256:398dbf2b0d12acaf54117a5210e8f191827f373d362d796091d161f610c1ebfb"}, + {file = "mkl-2021.4.0-py2.py3-none-win32.whl", hash = "sha256:439c640b269a5668134e3dcbcea4350459c4a8bc46469669b2d67e07e3d330e8"}, + {file = "mkl-2021.4.0-py2.py3-none-win_amd64.whl", hash = "sha256:ceef3cafce4c009dd25f65d7ad0d833a0fbadc3d8903991ec92351fe5de1e718"}, +] + +[package.dependencies] +intel-openmp = "==2021.*" +tbb = "==2021.*" + +[[package]] +name = "mpmath" +version = "1.3.0" +description = "Python library for arbitrary-precision floating-point arithmetic" +optional = false +python-versions = "*" +files = [ + {file = "mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c"}, + {file = "mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f"}, +] + +[package.extras] +develop = ["codecov", "pycodestyle", "pytest (>=4.6)", "pytest-cov", "wheel"] +docs = ["sphinx"] +gmpy = ["gmpy2 (>=2.1.0a4)"] +tests = ["pytest (>=4.6)"] + +[[package]] +name = "msgpack" +version = "1.0.8" +description = "MessagePack serializer" +optional = false +python-versions = ">=3.8" +files = [ + {file = "msgpack-1.0.8-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:505fe3d03856ac7d215dbe005414bc28505d26f0c128906037e66d98c4e95868"}, + {file = "msgpack-1.0.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6b7842518a63a9f17107eb176320960ec095a8ee3b4420b5f688e24bf50c53c"}, + {file = "msgpack-1.0.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:376081f471a2ef24828b83a641a02c575d6103a3ad7fd7dade5486cad10ea659"}, + {file = "msgpack-1.0.8-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e390971d082dba073c05dbd56322427d3280b7cc8b53484c9377adfbae67dc2"}, + {file = "msgpack-1.0.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00e073efcba9ea99db5acef3959efa45b52bc67b61b00823d2a1a6944bf45982"}, + {file = "msgpack-1.0.8-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82d92c773fbc6942a7a8b520d22c11cfc8fd83bba86116bfcf962c2f5c2ecdaa"}, + {file = "msgpack-1.0.8-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9ee32dcb8e531adae1f1ca568822e9b3a738369b3b686d1477cbc643c4a9c128"}, + {file = "msgpack-1.0.8-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e3aa7e51d738e0ec0afbed661261513b38b3014754c9459508399baf14ae0c9d"}, + {file = "msgpack-1.0.8-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:69284049d07fce531c17404fcba2bb1df472bc2dcdac642ae71a2d079d950653"}, + {file = "msgpack-1.0.8-cp310-cp310-win32.whl", hash = "sha256:13577ec9e247f8741c84d06b9ece5f654920d8365a4b636ce0e44f15e07ec693"}, + {file = "msgpack-1.0.8-cp310-cp310-win_amd64.whl", hash = "sha256:e532dbd6ddfe13946de050d7474e3f5fb6ec774fbb1a188aaf469b08cf04189a"}, + {file = "msgpack-1.0.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9517004e21664f2b5a5fd6333b0731b9cf0817403a941b393d89a2f1dc2bd836"}, + {file = "msgpack-1.0.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d16a786905034e7e34098634b184a7d81f91d4c3d246edc6bd7aefb2fd8ea6ad"}, + {file = "msgpack-1.0.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e2872993e209f7ed04d963e4b4fbae72d034844ec66bc4ca403329db2074377b"}, + {file = "msgpack-1.0.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c330eace3dd100bdb54b5653b966de7f51c26ec4a7d4e87132d9b4f738220ba"}, + {file = "msgpack-1.0.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83b5c044f3eff2a6534768ccfd50425939e7a8b5cf9a7261c385de1e20dcfc85"}, + {file = "msgpack-1.0.8-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1876b0b653a808fcd50123b953af170c535027bf1d053b59790eebb0aeb38950"}, + {file = "msgpack-1.0.8-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:dfe1f0f0ed5785c187144c46a292b8c34c1295c01da12e10ccddfc16def4448a"}, + {file = "msgpack-1.0.8-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:3528807cbbb7f315bb81959d5961855e7ba52aa60a3097151cb21956fbc7502b"}, + {file = "msgpack-1.0.8-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e2f879ab92ce502a1e65fce390eab619774dda6a6ff719718069ac94084098ce"}, + {file = "msgpack-1.0.8-cp311-cp311-win32.whl", hash = "sha256:26ee97a8261e6e35885c2ecd2fd4a6d38252246f94a2aec23665a4e66d066305"}, + {file = "msgpack-1.0.8-cp311-cp311-win_amd64.whl", hash = "sha256:eadb9f826c138e6cf3c49d6f8de88225a3c0ab181a9b4ba792e006e5292d150e"}, + {file = "msgpack-1.0.8-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:114be227f5213ef8b215c22dde19532f5da9652e56e8ce969bf0a26d7c419fee"}, + {file = "msgpack-1.0.8-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d661dc4785affa9d0edfdd1e59ec056a58b3dbb9f196fa43587f3ddac654ac7b"}, + {file = "msgpack-1.0.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d56fd9f1f1cdc8227d7b7918f55091349741904d9520c65f0139a9755952c9e8"}, + {file = "msgpack-1.0.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0726c282d188e204281ebd8de31724b7d749adebc086873a59efb8cf7ae27df3"}, + {file = "msgpack-1.0.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8db8e423192303ed77cff4dce3a4b88dbfaf43979d280181558af5e2c3c71afc"}, + {file = "msgpack-1.0.8-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:99881222f4a8c2f641f25703963a5cefb076adffd959e0558dc9f803a52d6a58"}, + {file = "msgpack-1.0.8-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b5505774ea2a73a86ea176e8a9a4a7c8bf5d521050f0f6f8426afe798689243f"}, + {file = "msgpack-1.0.8-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:ef254a06bcea461e65ff0373d8a0dd1ed3aa004af48839f002a0c994a6f72d04"}, + {file = "msgpack-1.0.8-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e1dd7839443592d00e96db831eddb4111a2a81a46b028f0facd60a09ebbdd543"}, + {file = "msgpack-1.0.8-cp312-cp312-win32.whl", hash = "sha256:64d0fcd436c5683fdd7c907eeae5e2cbb5eb872fafbc03a43609d7941840995c"}, + {file = "msgpack-1.0.8-cp312-cp312-win_amd64.whl", hash = "sha256:74398a4cf19de42e1498368c36eed45d9528f5fd0155241e82c4082b7e16cffd"}, + {file = "msgpack-1.0.8-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0ceea77719d45c839fd73abcb190b8390412a890df2f83fb8cf49b2a4b5c2f40"}, + {file = "msgpack-1.0.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1ab0bbcd4d1f7b6991ee7c753655b481c50084294218de69365f8f1970d4c151"}, + {file = "msgpack-1.0.8-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1cce488457370ffd1f953846f82323cb6b2ad2190987cd4d70b2713e17268d24"}, + {file = "msgpack-1.0.8-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3923a1778f7e5ef31865893fdca12a8d7dc03a44b33e2a5f3295416314c09f5d"}, + {file = "msgpack-1.0.8-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a22e47578b30a3e199ab067a4d43d790249b3c0587d9a771921f86250c8435db"}, + {file = "msgpack-1.0.8-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd739c9251d01e0279ce729e37b39d49a08c0420d3fee7f2a4968c0576678f77"}, + {file = "msgpack-1.0.8-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:d3420522057ebab1728b21ad473aa950026d07cb09da41103f8e597dfbfaeb13"}, + {file = "msgpack-1.0.8-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5845fdf5e5d5b78a49b826fcdc0eb2e2aa7191980e3d2cfd2a30303a74f212e2"}, + {file = "msgpack-1.0.8-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6a0e76621f6e1f908ae52860bdcb58e1ca85231a9b0545e64509c931dd34275a"}, + {file = "msgpack-1.0.8-cp38-cp38-win32.whl", hash = "sha256:374a8e88ddab84b9ada695d255679fb99c53513c0a51778796fcf0944d6c789c"}, + {file = "msgpack-1.0.8-cp38-cp38-win_amd64.whl", hash = "sha256:f3709997b228685fe53e8c433e2df9f0cdb5f4542bd5114ed17ac3c0129b0480"}, + {file = "msgpack-1.0.8-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f51bab98d52739c50c56658cc303f190785f9a2cd97b823357e7aeae54c8f68a"}, + {file = "msgpack-1.0.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:73ee792784d48aa338bba28063e19a27e8d989344f34aad14ea6e1b9bd83f596"}, + {file = "msgpack-1.0.8-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f9904e24646570539a8950400602d66d2b2c492b9010ea7e965025cb71d0c86d"}, + {file = "msgpack-1.0.8-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e75753aeda0ddc4c28dce4c32ba2f6ec30b1b02f6c0b14e547841ba5b24f753f"}, + {file = "msgpack-1.0.8-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5dbf059fb4b7c240c873c1245ee112505be27497e90f7c6591261c7d3c3a8228"}, + {file = "msgpack-1.0.8-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4916727e31c28be8beaf11cf117d6f6f188dcc36daae4e851fee88646f5b6b18"}, + {file = "msgpack-1.0.8-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7938111ed1358f536daf311be244f34df7bf3cdedb3ed883787aca97778b28d8"}, + {file = "msgpack-1.0.8-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:493c5c5e44b06d6c9268ce21b302c9ca055c1fd3484c25ba41d34476c76ee746"}, + {file = "msgpack-1.0.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5fbb160554e319f7b22ecf530a80a3ff496d38e8e07ae763b9e82fadfe96f273"}, + {file = "msgpack-1.0.8-cp39-cp39-win32.whl", hash = "sha256:f9af38a89b6a5c04b7d18c492c8ccf2aee7048aff1ce8437c4683bb5a1df893d"}, + {file = "msgpack-1.0.8-cp39-cp39-win_amd64.whl", hash = "sha256:ed59dd52075f8fc91da6053b12e8c89e37aa043f8986efd89e61fae69dc1b011"}, + {file = "msgpack-1.0.8.tar.gz", hash = "sha256:95c02b0e27e706e48d0e5426d1710ca78e0f0628d6e89d5b5a5b91a5f12274f3"}, +] + +[[package]] +name = "multidict" +version = "6.0.5" +description = "multidict implementation" +optional = false +python-versions = ">=3.7" +files = [ + {file = "multidict-6.0.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:228b644ae063c10e7f324ab1ab6b548bdf6f8b47f3ec234fef1093bc2735e5f9"}, + {file = "multidict-6.0.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:896ebdcf62683551312c30e20614305f53125750803b614e9e6ce74a96232604"}, + {file = "multidict-6.0.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:411bf8515f3be9813d06004cac41ccf7d1cd46dfe233705933dd163b60e37600"}, + {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d147090048129ce3c453f0292e7697d333db95e52616b3793922945804a433c"}, + {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:215ed703caf15f578dca76ee6f6b21b7603791ae090fbf1ef9d865571039ade5"}, + {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c6390cf87ff6234643428991b7359b5f59cc15155695deb4eda5c777d2b880f"}, + {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21fd81c4ebdb4f214161be351eb5bcf385426bf023041da2fd9e60681f3cebae"}, + {file = "multidict-6.0.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3cc2ad10255f903656017363cd59436f2111443a76f996584d1077e43ee51182"}, + {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6939c95381e003f54cd4c5516740faba40cf5ad3eeff460c3ad1d3e0ea2549bf"}, + {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:220dd781e3f7af2c2c1053da9fa96d9cf3072ca58f057f4c5adaaa1cab8fc442"}, + {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:766c8f7511df26d9f11cd3a8be623e59cca73d44643abab3f8c8c07620524e4a"}, + {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:fe5d7785250541f7f5019ab9cba2c71169dc7d74d0f45253f8313f436458a4ef"}, + {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c1c1496e73051918fcd4f58ff2e0f2f3066d1c76a0c6aeffd9b45d53243702cc"}, + {file = "multidict-6.0.5-cp310-cp310-win32.whl", hash = "sha256:7afcdd1fc07befad18ec4523a782cde4e93e0a2bf71239894b8d61ee578c1319"}, + {file = "multidict-6.0.5-cp310-cp310-win_amd64.whl", hash = "sha256:99f60d34c048c5c2fabc766108c103612344c46e35d4ed9ae0673d33c8fb26e8"}, + {file = "multidict-6.0.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f285e862d2f153a70586579c15c44656f888806ed0e5b56b64489afe4a2dbfba"}, + {file = "multidict-6.0.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:53689bb4e102200a4fafa9de9c7c3c212ab40a7ab2c8e474491914d2305f187e"}, + {file = "multidict-6.0.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:612d1156111ae11d14afaf3a0669ebf6c170dbb735e510a7438ffe2369a847fd"}, + {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7be7047bd08accdb7487737631d25735c9a04327911de89ff1b26b81745bd4e3"}, + {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de170c7b4fe6859beb8926e84f7d7d6c693dfe8e27372ce3b76f01c46e489fcf"}, + {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04bde7a7b3de05732a4eb39c94574db1ec99abb56162d6c520ad26f83267de29"}, + {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85f67aed7bb647f93e7520633d8f51d3cbc6ab96957c71272b286b2f30dc70ed"}, + {file = "multidict-6.0.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:425bf820055005bfc8aa9a0b99ccb52cc2f4070153e34b701acc98d201693733"}, + {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d3eb1ceec286eba8220c26f3b0096cf189aea7057b6e7b7a2e60ed36b373b77f"}, + {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7901c05ead4b3fb75113fb1dd33eb1253c6d3ee37ce93305acd9d38e0b5f21a4"}, + {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:e0e79d91e71b9867c73323a3444724d496c037e578a0e1755ae159ba14f4f3d1"}, + {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:29bfeb0dff5cb5fdab2023a7a9947b3b4af63e9c47cae2a10ad58394b517fddc"}, + {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e030047e85cbcedbfc073f71836d62dd5dadfbe7531cae27789ff66bc551bd5e"}, + {file = "multidict-6.0.5-cp311-cp311-win32.whl", hash = "sha256:2f4848aa3baa109e6ab81fe2006c77ed4d3cd1e0ac2c1fbddb7b1277c168788c"}, + {file = "multidict-6.0.5-cp311-cp311-win_amd64.whl", hash = "sha256:2faa5ae9376faba05f630d7e5e6be05be22913782b927b19d12b8145968a85ea"}, + {file = "multidict-6.0.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:51d035609b86722963404f711db441cf7134f1889107fb171a970c9701f92e1e"}, + {file = "multidict-6.0.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cbebcd5bcaf1eaf302617c114aa67569dd3f090dd0ce8ba9e35e9985b41ac35b"}, + {file = "multidict-6.0.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2ffc42c922dbfddb4a4c3b438eb056828719f07608af27d163191cb3e3aa6cc5"}, + {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ceb3b7e6a0135e092de86110c5a74e46bda4bd4fbfeeb3a3bcec79c0f861e450"}, + {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:79660376075cfd4b2c80f295528aa6beb2058fd289f4c9252f986751a4cd0496"}, + {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e4428b29611e989719874670fd152b6625500ad6c686d464e99f5aaeeaca175a"}, + {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d84a5c3a5f7ce6db1f999fb9438f686bc2e09d38143f2d93d8406ed2dd6b9226"}, + {file = "multidict-6.0.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:76c0de87358b192de7ea9649beb392f107dcad9ad27276324c24c91774ca5271"}, + {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:79a6d2ba910adb2cbafc95dad936f8b9386e77c84c35bc0add315b856d7c3abb"}, + {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:92d16a3e275e38293623ebf639c471d3e03bb20b8ebb845237e0d3664914caef"}, + {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:fb616be3538599e797a2017cccca78e354c767165e8858ab5116813146041a24"}, + {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:14c2976aa9038c2629efa2c148022ed5eb4cb939e15ec7aace7ca932f48f9ba6"}, + {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:435a0984199d81ca178b9ae2c26ec3d49692d20ee29bc4c11a2a8d4514c67eda"}, + {file = "multidict-6.0.5-cp312-cp312-win32.whl", hash = "sha256:9fe7b0653ba3d9d65cbe7698cca585bf0f8c83dbbcc710db9c90f478e175f2d5"}, + {file = "multidict-6.0.5-cp312-cp312-win_amd64.whl", hash = "sha256:01265f5e40f5a17f8241d52656ed27192be03bfa8764d88e8220141d1e4b3556"}, + {file = "multidict-6.0.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:19fe01cea168585ba0f678cad6f58133db2aa14eccaf22f88e4a6dccadfad8b3"}, + {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6bf7a982604375a8d49b6cc1b781c1747f243d91b81035a9b43a2126c04766f5"}, + {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:107c0cdefe028703fb5dafe640a409cb146d44a6ae201e55b35a4af8e95457dd"}, + {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:403c0911cd5d5791605808b942c88a8155c2592e05332d2bf78f18697a5fa15e"}, + {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aeaf541ddbad8311a87dd695ed9642401131ea39ad7bc8cf3ef3967fd093b626"}, + {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e4972624066095e52b569e02b5ca97dbd7a7ddd4294bf4e7247d52635630dd83"}, + {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d946b0a9eb8aaa590df1fe082cee553ceab173e6cb5b03239716338629c50c7a"}, + {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b55358304d7a73d7bdf5de62494aaf70bd33015831ffd98bc498b433dfe5b10c"}, + {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:a3145cb08d8625b2d3fee1b2d596a8766352979c9bffe5d7833e0503d0f0b5e5"}, + {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:d65f25da8e248202bd47445cec78e0025c0fe7582b23ec69c3b27a640dd7a8e3"}, + {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c9bf56195c6bbd293340ea82eafd0071cb3d450c703d2c93afb89f93b8386ccc"}, + {file = "multidict-6.0.5-cp37-cp37m-win32.whl", hash = "sha256:69db76c09796b313331bb7048229e3bee7928eb62bab5e071e9f7fcc4879caee"}, + {file = "multidict-6.0.5-cp37-cp37m-win_amd64.whl", hash = "sha256:fce28b3c8a81b6b36dfac9feb1de115bab619b3c13905b419ec71d03a3fc1423"}, + {file = "multidict-6.0.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:76f067f5121dcecf0d63a67f29080b26c43c71a98b10c701b0677e4a065fbd54"}, + {file = "multidict-6.0.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b82cc8ace10ab5bd93235dfaab2021c70637005e1ac787031f4d1da63d493c1d"}, + {file = "multidict-6.0.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5cb241881eefd96b46f89b1a056187ea8e9ba14ab88ba632e68d7a2ecb7aadf7"}, + {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8e94e6912639a02ce173341ff62cc1201232ab86b8a8fcc05572741a5dc7d93"}, + {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09a892e4a9fb47331da06948690ae38eaa2426de97b4ccbfafbdcbe5c8f37ff8"}, + {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55205d03e8a598cfc688c71ca8ea5f66447164efff8869517f175ea632c7cb7b"}, + {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37b15024f864916b4951adb95d3a80c9431299080341ab9544ed148091b53f50"}, + {file = "multidict-6.0.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2a1dee728b52b33eebff5072817176c172050d44d67befd681609b4746e1c2e"}, + {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:edd08e6f2f1a390bf137080507e44ccc086353c8e98c657e666c017718561b89"}, + {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:60d698e8179a42ec85172d12f50b1668254628425a6bd611aba022257cac1386"}, + {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:3d25f19500588cbc47dc19081d78131c32637c25804df8414463ec908631e453"}, + {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:4cc0ef8b962ac7a5e62b9e826bd0cd5040e7d401bc45a6835910ed699037a461"}, + {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:eca2e9d0cc5a889850e9bbd68e98314ada174ff6ccd1129500103df7a94a7a44"}, + {file = "multidict-6.0.5-cp38-cp38-win32.whl", hash = "sha256:4a6a4f196f08c58c59e0b8ef8ec441d12aee4125a7d4f4fef000ccb22f8d7241"}, + {file = "multidict-6.0.5-cp38-cp38-win_amd64.whl", hash = "sha256:0275e35209c27a3f7951e1ce7aaf93ce0d163b28948444bec61dd7badc6d3f8c"}, + {file = "multidict-6.0.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e7be68734bd8c9a513f2b0cfd508802d6609da068f40dc57d4e3494cefc92929"}, + {file = "multidict-6.0.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1d9ea7a7e779d7a3561aade7d596649fbecfa5c08a7674b11b423783217933f9"}, + {file = "multidict-6.0.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ea1456df2a27c73ce51120fa2f519f1bea2f4a03a917f4a43c8707cf4cbbae1a"}, + {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf590b134eb70629e350691ecca88eac3e3b8b3c86992042fb82e3cb1830d5e1"}, + {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5c0631926c4f58e9a5ccce555ad7747d9a9f8b10619621f22f9635f069f6233e"}, + {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dce1c6912ab9ff5f179eaf6efe7365c1f425ed690b03341911bf4939ef2f3046"}, + {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0868d64af83169e4d4152ec612637a543f7a336e4a307b119e98042e852ad9c"}, + {file = "multidict-6.0.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:141b43360bfd3bdd75f15ed811850763555a251e38b2405967f8e25fb43f7d40"}, + {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7df704ca8cf4a073334e0427ae2345323613e4df18cc224f647f251e5e75a527"}, + {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:6214c5a5571802c33f80e6c84713b2c79e024995b9c5897f794b43e714daeec9"}, + {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:cd6c8fca38178e12c00418de737aef1261576bd1b6e8c6134d3e729a4e858b38"}, + {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:e02021f87a5b6932fa6ce916ca004c4d441509d33bbdbeca70d05dff5e9d2479"}, + {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ebd8d160f91a764652d3e51ce0d2956b38efe37c9231cd82cfc0bed2e40b581c"}, + {file = "multidict-6.0.5-cp39-cp39-win32.whl", hash = "sha256:04da1bb8c8dbadf2a18a452639771951c662c5ad03aefe4884775454be322c9b"}, + {file = "multidict-6.0.5-cp39-cp39-win_amd64.whl", hash = "sha256:d6f6d4f185481c9669b9447bf9d9cf3b95a0e9df9d169bbc17e363b7d5487755"}, + {file = "multidict-6.0.5-py3-none-any.whl", hash = "sha256:0d63c74e3d7ab26de115c49bffc92cc77ed23395303d496eae515d4204a625e7"}, + {file = "multidict-6.0.5.tar.gz", hash = "sha256:f7e301075edaf50500f0b341543c41194d8df3ae5caf4702f2095f3ca73dd8da"}, +] + +[[package]] +name = "mypy" +version = "1.11.1" +description = "Optional static typing for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "mypy-1.11.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a32fc80b63de4b5b3e65f4be82b4cfa362a46702672aa6a0f443b4689af7008c"}, + {file = "mypy-1.11.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c1952f5ea8a5a959b05ed5f16452fddadbaae48b5d39235ab4c3fc444d5fd411"}, + {file = "mypy-1.11.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1e30dc3bfa4e157e53c1d17a0dad20f89dc433393e7702b813c10e200843b03"}, + {file = "mypy-1.11.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2c63350af88f43a66d3dfeeeb8d77af34a4f07d760b9eb3a8697f0386c7590b4"}, + {file = "mypy-1.11.1-cp310-cp310-win_amd64.whl", hash = "sha256:a831671bad47186603872a3abc19634f3011d7f83b083762c942442d51c58d58"}, + {file = "mypy-1.11.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7b6343d338390bb946d449677726edf60102a1c96079b4f002dedff375953fc5"}, + {file = "mypy-1.11.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e4fe9f4e5e521b458d8feb52547f4bade7ef8c93238dfb5bbc790d9ff2d770ca"}, + {file = "mypy-1.11.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:886c9dbecc87b9516eff294541bf7f3655722bf22bb898ee06985cd7269898de"}, + {file = "mypy-1.11.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fca4a60e1dd9fd0193ae0067eaeeb962f2d79e0d9f0f66223a0682f26ffcc809"}, + {file = "mypy-1.11.1-cp311-cp311-win_amd64.whl", hash = "sha256:0bd53faf56de9643336aeea1c925012837432b5faf1701ccca7fde70166ccf72"}, + {file = "mypy-1.11.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f39918a50f74dc5969807dcfaecafa804fa7f90c9d60506835036cc1bc891dc8"}, + {file = "mypy-1.11.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0bc71d1fb27a428139dd78621953effe0d208aed9857cb08d002280b0422003a"}, + {file = "mypy-1.11.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b868d3bcff720dd7217c383474008ddabaf048fad8d78ed948bb4b624870a417"}, + {file = "mypy-1.11.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a707ec1527ffcdd1c784d0924bf5cb15cd7f22683b919668a04d2b9c34549d2e"}, + {file = "mypy-1.11.1-cp312-cp312-win_amd64.whl", hash = "sha256:64f4a90e3ea07f590c5bcf9029035cf0efeae5ba8be511a8caada1a4893f5525"}, + {file = "mypy-1.11.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:749fd3213916f1751fff995fccf20c6195cae941dc968f3aaadf9bb4e430e5a2"}, + {file = "mypy-1.11.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b639dce63a0b19085213ec5fdd8cffd1d81988f47a2dec7100e93564f3e8fb3b"}, + {file = "mypy-1.11.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c956b49c5d865394d62941b109728c5c596a415e9c5b2be663dd26a1ff07bc0"}, + {file = "mypy-1.11.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:45df906e8b6804ef4b666af29a87ad9f5921aad091c79cc38e12198e220beabd"}, + {file = "mypy-1.11.1-cp38-cp38-win_amd64.whl", hash = "sha256:d44be7551689d9d47b7abc27c71257adfdb53f03880841a5db15ddb22dc63edb"}, + {file = "mypy-1.11.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2684d3f693073ab89d76da8e3921883019ea8a3ec20fa5d8ecca6a2db4c54bbe"}, + {file = "mypy-1.11.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:79c07eb282cb457473add5052b63925e5cc97dfab9812ee65a7c7ab5e3cb551c"}, + {file = "mypy-1.11.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11965c2f571ded6239977b14deebd3f4c3abd9a92398712d6da3a772974fad69"}, + {file = "mypy-1.11.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a2b43895a0f8154df6519706d9bca8280cda52d3d9d1514b2d9c3e26792a0b74"}, + {file = "mypy-1.11.1-cp39-cp39-win_amd64.whl", hash = "sha256:1a81cf05975fd61aec5ae16501a091cfb9f605dc3e3c878c0da32f250b74760b"}, + {file = "mypy-1.11.1-py3-none-any.whl", hash = "sha256:0624bdb940255d2dd24e829d99a13cfeb72e4e9031f9492148f410ed30bcab54"}, + {file = "mypy-1.11.1.tar.gz", hash = "sha256:f404a0b069709f18bbdb702eb3dcfe51910602995de00bd39cea3050b5772d08"}, +] + +[package.dependencies] +mypy-extensions = ">=1.0.0" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typing-extensions = ">=4.6.0" + +[package.extras] +dmypy = ["psutil (>=4.0)"] +install-types = ["pip"] +mypyc = ["setuptools (>=50)"] +reports = ["lxml"] + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +description = "Type system extensions for programs checked with the mypy type checker." +optional = false +python-versions = ">=3.5" +files = [ + {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, + {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, +] + +[[package]] +name = "nbformat" +version = "5.10.4" +description = "The Jupyter Notebook format" +optional = false +python-versions = ">=3.8" +files = [ + {file = "nbformat-5.10.4-py3-none-any.whl", hash = "sha256:3b48d6c8fbca4b299bf3982ea7db1af21580e4fec269ad087b9e81588891200b"}, + {file = "nbformat-5.10.4.tar.gz", hash = "sha256:322168b14f937a5d11362988ecac2a4952d3d8e3a2cbeb2319584631226d5b3a"}, +] + +[package.dependencies] +fastjsonschema = ">=2.15" +jsonschema = ">=2.6" +jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0" +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 = "nbqa" +version = "1.8.7" +description = "Run any standard Python code quality tool on a Jupyter Notebook" +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "nbqa-1.8.7-py3-none-any.whl", hash = "sha256:36c6f8fced65031ae3d90f5e0d56708cc4e8ee2b5cdddd9a96a1d3b8afe6d21b"}, + {file = "nbqa-1.8.7.tar.gz", hash = "sha256:8122e2cb83df724dcfa788785b3b5336ae3c28535b62f87fb4961011a72423dc"}, +] + +[package.dependencies] +autopep8 = ">=1.5" +black = {version = "*", optional = true, markers = "extra == \"toolchain\""} +blacken-docs = {version = "*", optional = true, markers = "extra == \"toolchain\""} +flake8 = {version = "*", optional = true, markers = "extra == \"toolchain\""} +ipython = ">=7.8.0" +isort = {version = "*", optional = true, markers = "extra == \"toolchain\""} +jupytext = {version = "*", optional = true, markers = "extra == \"toolchain\""} +mypy = {version = "*", optional = true, markers = "extra == \"toolchain\""} +pylint = {version = "*", optional = true, markers = "extra == \"toolchain\""} +pyupgrade = {version = "*", optional = true, markers = "extra == \"toolchain\""} +ruff = {version = "*", optional = true, markers = "extra == \"toolchain\""} +tokenize-rt = ">=3.2.0" +tomli = "*" + +[package.extras] +toolchain = ["black", "blacken-docs", "flake8", "isort", "jupytext", "mypy", "pylint", "pyupgrade", "ruff"] + +[[package]] +name = "nest-asyncio" +version = "1.6.0" +description = "Patch asyncio to allow nested event loops" +optional = false +python-versions = ">=3.5" +files = [ + {file = "nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c"}, + {file = "nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe"}, +] + +[[package]] +name = "networkx" +version = "3.2.1" +description = "Python package for creating and manipulating graphs and networks" +optional = false +python-versions = ">=3.9" +files = [ + {file = "networkx-3.2.1-py3-none-any.whl", hash = "sha256:f18c69adc97877c42332c170849c96cefa91881c99a7cb3e95b7c659ebdc1ec2"}, + {file = "networkx-3.2.1.tar.gz", hash = "sha256:9f1bb5cf3409bf324e0a722c20bdb4c20ee39bf1c30ce8ae499c8502b0b5e0c6"}, +] + +[package.extras] +default = ["matplotlib (>=3.5)", "numpy (>=1.22)", "pandas (>=1.4)", "scipy (>=1.9,!=1.11.0,!=1.11.1)"] +developer = ["changelist (==0.4)", "mypy (>=1.1)", "pre-commit (>=3.2)", "rtoml"] +doc = ["nb2plots (>=0.7)", "nbconvert (<7.9)", "numpydoc (>=1.6)", "pillow (>=9.4)", "pydata-sphinx-theme (>=0.14)", "sphinx (>=7)", "sphinx-gallery (>=0.14)", "texext (>=0.6.7)"] +extra = ["lxml (>=4.6)", "pydot (>=1.4.2)", "pygraphviz (>=1.11)", "sympy (>=1.10)"] +test = ["pytest (>=7.2)", "pytest-cov (>=4.0)"] + +[[package]] +name = "nodeenv" +version = "1.9.1" +description = "Node.js virtual environment builder" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"}, + {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"}, +] + +[[package]] +name = "numpy" +version = "2.0.1" +description = "Fundamental package for array computing in Python" +optional = false +python-versions = ">=3.9" +files = [ + {file = "numpy-2.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0fbb536eac80e27a2793ffd787895242b7f18ef792563d742c2d673bfcb75134"}, + {file = "numpy-2.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:69ff563d43c69b1baba77af455dd0a839df8d25e8590e79c90fcbe1499ebde42"}, + {file = "numpy-2.0.1-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:1b902ce0e0a5bb7704556a217c4f63a7974f8f43e090aff03fcf262e0b135e02"}, + {file = "numpy-2.0.1-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:f1659887361a7151f89e79b276ed8dff3d75877df906328f14d8bb40bb4f5101"}, + {file = "numpy-2.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4658c398d65d1b25e1760de3157011a80375da861709abd7cef3bad65d6543f9"}, + {file = "numpy-2.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4127d4303b9ac9f94ca0441138acead39928938660ca58329fe156f84b9f3015"}, + {file = "numpy-2.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e5eeca8067ad04bc8a2a8731183d51d7cbaac66d86085d5f4766ee6bf19c7f87"}, + {file = "numpy-2.0.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:9adbd9bb520c866e1bfd7e10e1880a1f7749f1f6e5017686a5fbb9b72cf69f82"}, + {file = "numpy-2.0.1-cp310-cp310-win32.whl", hash = "sha256:7b9853803278db3bdcc6cd5beca37815b133e9e77ff3d4733c247414e78eb8d1"}, + {file = "numpy-2.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:81b0893a39bc5b865b8bf89e9ad7807e16717f19868e9d234bdaf9b1f1393868"}, + {file = "numpy-2.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:75b4e316c5902d8163ef9d423b1c3f2f6252226d1aa5cd8a0a03a7d01ffc6268"}, + {file = "numpy-2.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6e4eeb6eb2fced786e32e6d8df9e755ce5be920d17f7ce00bc38fcde8ccdbf9e"}, + {file = "numpy-2.0.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:a1e01dcaab205fbece13c1410253a9eea1b1c9b61d237b6fa59bcc46e8e89343"}, + {file = "numpy-2.0.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:a8fc2de81ad835d999113ddf87d1ea2b0f4704cbd947c948d2f5513deafe5a7b"}, + {file = "numpy-2.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5a3d94942c331dd4e0e1147f7a8699a4aa47dffc11bf8a1523c12af8b2e91bbe"}, + {file = "numpy-2.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15eb4eca47d36ec3f78cde0a3a2ee24cf05ca7396ef808dda2c0ddad7c2bde67"}, + {file = "numpy-2.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b83e16a5511d1b1f8a88cbabb1a6f6a499f82c062a4251892d9ad5d609863fb7"}, + {file = "numpy-2.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1f87fec1f9bc1efd23f4227becff04bd0e979e23ca50cc92ec88b38489db3b55"}, + {file = "numpy-2.0.1-cp311-cp311-win32.whl", hash = "sha256:36d3a9405fd7c511804dc56fc32974fa5533bdeb3cd1604d6b8ff1d292b819c4"}, + {file = "numpy-2.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:08458fbf403bff5e2b45f08eda195d4b0c9b35682311da5a5a0a0925b11b9bd8"}, + {file = "numpy-2.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6bf4e6f4a2a2e26655717a1983ef6324f2664d7011f6ef7482e8c0b3d51e82ac"}, + {file = "numpy-2.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7d6fddc5fe258d3328cd8e3d7d3e02234c5d70e01ebe377a6ab92adb14039cb4"}, + {file = "numpy-2.0.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:5daab361be6ddeb299a918a7c0864fa8618af66019138263247af405018b04e1"}, + {file = "numpy-2.0.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:ea2326a4dca88e4a274ba3a4405eb6c6467d3ffbd8c7d38632502eaae3820587"}, + {file = "numpy-2.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:529af13c5f4b7a932fb0e1911d3a75da204eff023ee5e0e79c1751564221a5c8"}, + {file = "numpy-2.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6790654cb13eab303d8402354fabd47472b24635700f631f041bd0b65e37298a"}, + {file = "numpy-2.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:cbab9fc9c391700e3e1287666dfd82d8666d10e69a6c4a09ab97574c0b7ee0a7"}, + {file = "numpy-2.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:99d0d92a5e3613c33a5f01db206a33f8fdf3d71f2912b0de1739894668b7a93b"}, + {file = "numpy-2.0.1-cp312-cp312-win32.whl", hash = "sha256:173a00b9995f73b79eb0191129f2455f1e34c203f559dd118636858cc452a1bf"}, + {file = "numpy-2.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:bb2124fdc6e62baae159ebcfa368708867eb56806804d005860b6007388df171"}, + {file = "numpy-2.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bfc085b28d62ff4009364e7ca34b80a9a080cbd97c2c0630bb5f7f770dae9414"}, + {file = "numpy-2.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8fae4ebbf95a179c1156fab0b142b74e4ba4204c87bde8d3d8b6f9c34c5825ef"}, + {file = "numpy-2.0.1-cp39-cp39-macosx_14_0_arm64.whl", hash = "sha256:72dc22e9ec8f6eaa206deb1b1355eb2e253899d7347f5e2fae5f0af613741d06"}, + {file = "numpy-2.0.1-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:ec87f5f8aca726117a1c9b7083e7656a9d0d606eec7299cc067bb83d26f16e0c"}, + {file = "numpy-2.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f682ea61a88479d9498bf2091fdcd722b090724b08b31d63e022adc063bad59"}, + {file = "numpy-2.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8efc84f01c1cd7e34b3fb310183e72fcdf55293ee736d679b6d35b35d80bba26"}, + {file = "numpy-2.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3fdabe3e2a52bc4eff8dc7a5044342f8bd9f11ef0934fcd3289a788c0eb10018"}, + {file = "numpy-2.0.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:24a0e1befbfa14615b49ba9659d3d8818a0f4d8a1c5822af8696706fbda7310c"}, + {file = "numpy-2.0.1-cp39-cp39-win32.whl", hash = "sha256:f9cf5ea551aec449206954b075db819f52adc1638d46a6738253a712d553c7b4"}, + {file = "numpy-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:e9e81fa9017eaa416c056e5d9e71be93d05e2c3c2ab308d23307a8bc4443c368"}, + {file = "numpy-2.0.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:61728fba1e464f789b11deb78a57805c70b2ed02343560456190d0501ba37b0f"}, + {file = "numpy-2.0.1-pp39-pypy39_pp73-macosx_14_0_x86_64.whl", hash = "sha256:12f5d865d60fb9734e60a60f1d5afa6d962d8d4467c120a1c0cda6eb2964437d"}, + {file = "numpy-2.0.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eacf3291e263d5a67d8c1a581a8ebbcfd6447204ef58828caf69a5e3e8c75990"}, + {file = "numpy-2.0.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2c3a346ae20cfd80b6cfd3e60dc179963ef2ea58da5ec074fd3d9e7a1e7ba97f"}, + {file = "numpy-2.0.1.tar.gz", hash = "sha256:485b87235796410c3519a699cfe1faab097e509e90ebb05dcd098db2ae87e7b3"}, +] + +[[package]] +name = "nvidia-cublas-cu12" +version = "12.1.3.1" +description = "CUBLAS native runtime libraries" +optional = false +python-versions = ">=3" +files = [ + {file = "nvidia_cublas_cu12-12.1.3.1-py3-none-manylinux1_x86_64.whl", hash = "sha256:ee53ccca76a6fc08fb9701aa95b6ceb242cdaab118c3bb152af4e579af792728"}, + {file = "nvidia_cublas_cu12-12.1.3.1-py3-none-win_amd64.whl", hash = "sha256:2b964d60e8cf11b5e1073d179d85fa340c120e99b3067558f3cf98dd69d02906"}, +] + +[[package]] +name = "nvidia-cuda-cupti-cu12" +version = "12.1.105" +description = "CUDA profiling tools runtime libs." +optional = false +python-versions = ">=3" +files = [ + {file = "nvidia_cuda_cupti_cu12-12.1.105-py3-none-manylinux1_x86_64.whl", hash = "sha256:e54fde3983165c624cb79254ae9818a456eb6e87a7fd4d56a2352c24ee542d7e"}, + {file = "nvidia_cuda_cupti_cu12-12.1.105-py3-none-win_amd64.whl", hash = "sha256:bea8236d13a0ac7190bd2919c3e8e6ce1e402104276e6f9694479e48bb0eb2a4"}, +] + +[[package]] +name = "nvidia-cuda-nvrtc-cu12" +version = "12.1.105" +description = "NVRTC native runtime libraries" +optional = false +python-versions = ">=3" +files = [ + {file = "nvidia_cuda_nvrtc_cu12-12.1.105-py3-none-manylinux1_x86_64.whl", hash = "sha256:339b385f50c309763ca65456ec75e17bbefcbbf2893f462cb8b90584cd27a1c2"}, + {file = "nvidia_cuda_nvrtc_cu12-12.1.105-py3-none-win_amd64.whl", hash = "sha256:0a98a522d9ff138b96c010a65e145dc1b4850e9ecb75a0172371793752fd46ed"}, +] + +[[package]] +name = "nvidia-cuda-runtime-cu12" +version = "12.1.105" +description = "CUDA Runtime native Libraries" +optional = false +python-versions = ">=3" +files = [ + {file = "nvidia_cuda_runtime_cu12-12.1.105-py3-none-manylinux1_x86_64.whl", hash = "sha256:6e258468ddf5796e25f1dc591a31029fa317d97a0a94ed93468fc86301d61e40"}, + {file = "nvidia_cuda_runtime_cu12-12.1.105-py3-none-win_amd64.whl", hash = "sha256:dfb46ef84d73fababab44cf03e3b83f80700d27ca300e537f85f636fac474344"}, +] + +[[package]] +name = "nvidia-cudnn-cu12" +version = "8.9.2.26" +description = "cuDNN runtime libraries" +optional = false +python-versions = ">=3" +files = [ + {file = "nvidia_cudnn_cu12-8.9.2.26-py3-none-manylinux1_x86_64.whl", hash = "sha256:5ccb288774fdfb07a7e7025ffec286971c06d8d7b4fb162525334616d7629ff9"}, +] + +[package.dependencies] +nvidia-cublas-cu12 = "*" + +[[package]] +name = "nvidia-cufft-cu12" +version = "11.0.2.54" +description = "CUFFT native runtime libraries" +optional = false +python-versions = ">=3" +files = [ + {file = "nvidia_cufft_cu12-11.0.2.54-py3-none-manylinux1_x86_64.whl", hash = "sha256:794e3948a1aa71fd817c3775866943936774d1c14e7628c74f6f7417224cdf56"}, + {file = "nvidia_cufft_cu12-11.0.2.54-py3-none-win_amd64.whl", hash = "sha256:d9ac353f78ff89951da4af698f80870b1534ed69993f10a4cf1d96f21357e253"}, +] + +[[package]] +name = "nvidia-curand-cu12" +version = "10.3.2.106" +description = "CURAND native runtime libraries" +optional = false +python-versions = ">=3" +files = [ + {file = "nvidia_curand_cu12-10.3.2.106-py3-none-manylinux1_x86_64.whl", hash = "sha256:9d264c5036dde4e64f1de8c50ae753237c12e0b1348738169cd0f8a536c0e1e0"}, + {file = "nvidia_curand_cu12-10.3.2.106-py3-none-win_amd64.whl", hash = "sha256:75b6b0c574c0037839121317e17fd01f8a69fd2ef8e25853d826fec30bdba74a"}, +] + +[[package]] +name = "nvidia-cusolver-cu12" +version = "11.4.5.107" +description = "CUDA solver native runtime libraries" +optional = false +python-versions = ">=3" +files = [ + {file = "nvidia_cusolver_cu12-11.4.5.107-py3-none-manylinux1_x86_64.whl", hash = "sha256:8a7ec542f0412294b15072fa7dab71d31334014a69f953004ea7a118206fe0dd"}, + {file = "nvidia_cusolver_cu12-11.4.5.107-py3-none-win_amd64.whl", hash = "sha256:74e0c3a24c78612192a74fcd90dd117f1cf21dea4822e66d89e8ea80e3cd2da5"}, +] + +[package.dependencies] +nvidia-cublas-cu12 = "*" +nvidia-cusparse-cu12 = "*" +nvidia-nvjitlink-cu12 = "*" + +[[package]] +name = "nvidia-cusparse-cu12" +version = "12.1.0.106" +description = "CUSPARSE native runtime libraries" +optional = false +python-versions = ">=3" +files = [ + {file = "nvidia_cusparse_cu12-12.1.0.106-py3-none-manylinux1_x86_64.whl", hash = "sha256:f3b50f42cf363f86ab21f720998517a659a48131e8d538dc02f8768237bd884c"}, + {file = "nvidia_cusparse_cu12-12.1.0.106-py3-none-win_amd64.whl", hash = "sha256:b798237e81b9719373e8fae8d4f091b70a0cf09d9d85c95a557e11df2d8e9a5a"}, +] + +[package.dependencies] +nvidia-nvjitlink-cu12 = "*" + +[[package]] +name = "nvidia-nccl-cu12" +version = "2.20.5" +description = "NVIDIA Collective Communication Library (NCCL) Runtime" +optional = false +python-versions = ">=3" +files = [ + {file = "nvidia_nccl_cu12-2.20.5-py3-none-manylinux2014_aarch64.whl", hash = "sha256:1fc150d5c3250b170b29410ba682384b14581db722b2531b0d8d33c595f33d01"}, + {file = "nvidia_nccl_cu12-2.20.5-py3-none-manylinux2014_x86_64.whl", hash = "sha256:057f6bf9685f75215d0c53bf3ac4a10b3e6578351de307abad9e18a99182af56"}, +] + +[[package]] +name = "nvidia-nvjitlink-cu12" +version = "12.6.20" +description = "Nvidia JIT LTO Library" +optional = false +python-versions = ">=3" +files = [ + {file = "nvidia_nvjitlink_cu12-12.6.20-py3-none-manylinux2014_aarch64.whl", hash = "sha256:84fb38465a5bc7c70cbc320cfd0963eb302ee25a5e939e9f512bbba55b6072fb"}, + {file = "nvidia_nvjitlink_cu12-12.6.20-py3-none-manylinux2014_x86_64.whl", hash = "sha256:562ab97ea2c23164823b2a89cb328d01d45cb99634b8c65fe7cd60d14562bd79"}, + {file = "nvidia_nvjitlink_cu12-12.6.20-py3-none-win_amd64.whl", hash = "sha256:ed3c43a17f37b0c922a919203d2d36cbef24d41cc3e6b625182f8b58203644f6"}, +] + +[[package]] +name = "nvidia-nvtx-cu12" +version = "12.1.105" +description = "NVIDIA Tools Extension" +optional = false +python-versions = ">=3" +files = [ + {file = "nvidia_nvtx_cu12-12.1.105-py3-none-manylinux1_x86_64.whl", hash = "sha256:dc21cf308ca5691e7c04d962e213f8a4aa9bbfa23d95412f452254c2caeb09e5"}, + {file = "nvidia_nvtx_cu12-12.1.105-py3-none-win_amd64.whl", hash = "sha256:65f4d98982b31b60026e0e6de73fbdfc09d08a96f4656dd3665ca616a11e1e82"}, +] + +[[package]] +name = "omegaconf" +version = "2.3.0" +description = "A flexible configuration library" +optional = false +python-versions = ">=3.6" +files = [ + {file = "omegaconf-2.3.0-py3-none-any.whl", hash = "sha256:7b4df175cdb08ba400f45cae3bdcae7ba8365db4d165fc65fd04b050ab63b46b"}, + {file = "omegaconf-2.3.0.tar.gz", hash = "sha256:d5d4b6d29955cc50ad50c46dc269bcd92c6e00f5f90d23ab5fee7bfca4ba4cc7"}, +] + +[package.dependencies] +antlr4-python3-runtime = "==4.9.*" +PyYAML = ">=5.1.0" + +[[package]] +name = "opencv-python" +version = "4.10.0.84" +description = "Wrapper package for OpenCV python bindings." +optional = false +python-versions = ">=3.6" +files = [ + {file = "opencv-python-4.10.0.84.tar.gz", hash = "sha256:72d234e4582e9658ffea8e9cae5b63d488ad06994ef12d81dc303b17472f3526"}, + {file = "opencv_python-4.10.0.84-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:fc182f8f4cda51b45f01c64e4cbedfc2f00aff799debebc305d8d0210c43f251"}, + {file = "opencv_python-4.10.0.84-cp37-abi3-macosx_12_0_x86_64.whl", hash = "sha256:71e575744f1d23f79741450254660442785f45a0797212852ee5199ef12eed98"}, + {file = "opencv_python-4.10.0.84-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09a332b50488e2dda866a6c5573ee192fe3583239fb26ff2f7f9ceb0bc119ea6"}, + {file = "opencv_python-4.10.0.84-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ace140fc6d647fbe1c692bcb2abce768973491222c067c131d80957c595b71f"}, + {file = "opencv_python-4.10.0.84-cp37-abi3-win32.whl", hash = "sha256:2db02bb7e50b703f0a2d50c50ced72e95c574e1e5a0bb35a8a86d0b35c98c236"}, + {file = "opencv_python-4.10.0.84-cp37-abi3-win_amd64.whl", hash = "sha256:32dbbd94c26f611dc5cc6979e6b7aa1f55a64d6b463cc1dcd3c95505a63e48fe"}, +] + +[package.dependencies] +numpy = [ + {version = ">=1.21.0", markers = "python_version == \"3.9\" and platform_system == \"Darwin\" and platform_machine == \"arm64\""}, + {version = ">=1.19.3", markers = "platform_system == \"Linux\" and platform_machine == \"aarch64\" and python_version >= \"3.8\" and python_version < \"3.10\" or python_version > \"3.9\" and python_version < \"3.10\" or python_version >= \"3.9\" and platform_system != \"Darwin\" and python_version < \"3.10\" or python_version >= \"3.9\" and platform_machine != \"arm64\" and python_version < \"3.10\""}, + {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, + {version = ">=1.23.5", markers = "python_version >= \"3.11\" and python_version < \"3.12\""}, + {version = ">=1.21.4", markers = "python_version >= \"3.10\" and platform_system == \"Darwin\" and python_version < \"3.11\""}, + {version = ">=1.21.2", markers = "platform_system != \"Darwin\" and python_version >= \"3.10\" and python_version < \"3.11\""}, +] + +[[package]] +name = "packageurl-python" +version = "0.15.6" +description = "A purl aka. Package URL parser and builder" +optional = false +python-versions = ">=3.7" +files = [ + {file = "packageurl_python-0.15.6-py3-none-any.whl", hash = "sha256:a40210652c89022772a6c8340d6066f7d5dc67132141e5284a4db7a27d0a8ab0"}, + {file = "packageurl_python-0.15.6.tar.gz", hash = "sha256:cbc89afd15d5f4d05db4f1b61297e5b97a43f61f28799f6d282aff467ed2ee96"}, +] + +[package.extras] +build = ["setuptools", "wheel"] +lint = ["black", "isort", "mypy"] +sqlalchemy = ["sqlalchemy (>=2.0.0)"] +test = ["pytest"] + +[[package]] +name = "packaging" +version = "24.1" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, + {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, +] + +[[package]] +name = "pandas" +version = "2.2.2" +description = "Powerful data structures for data analysis, time series, and statistics" +optional = false +python-versions = ">=3.9" +files = [ + {file = "pandas-2.2.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:90c6fca2acf139569e74e8781709dccb6fe25940488755716d1d354d6bc58bce"}, + {file = "pandas-2.2.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c7adfc142dac335d8c1e0dcbd37eb8617eac386596eb9e1a1b77791cf2498238"}, + {file = "pandas-2.2.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4abfe0be0d7221be4f12552995e58723c7422c80a659da13ca382697de830c08"}, + {file = "pandas-2.2.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8635c16bf3d99040fdf3ca3db669a7250ddf49c55dc4aa8fe0ae0fa8d6dcc1f0"}, + {file = "pandas-2.2.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:40ae1dffb3967a52203105a077415a86044a2bea011b5f321c6aa64b379a3f51"}, + {file = "pandas-2.2.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8e5a0b00e1e56a842f922e7fae8ae4077aee4af0acb5ae3622bd4b4c30aedf99"}, + {file = "pandas-2.2.2-cp310-cp310-win_amd64.whl", hash = "sha256:ddf818e4e6c7c6f4f7c8a12709696d193976b591cc7dc50588d3d1a6b5dc8772"}, + {file = "pandas-2.2.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:696039430f7a562b74fa45f540aca068ea85fa34c244d0deee539cb6d70aa288"}, + {file = "pandas-2.2.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8e90497254aacacbc4ea6ae5e7a8cd75629d6ad2b30025a4a8b09aa4faf55151"}, + {file = "pandas-2.2.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:58b84b91b0b9f4bafac2a0ac55002280c094dfc6402402332c0913a59654ab2b"}, + {file = "pandas-2.2.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d2123dc9ad6a814bcdea0f099885276b31b24f7edf40f6cdbc0912672e22eee"}, + {file = "pandas-2.2.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:2925720037f06e89af896c70bca73459d7e6a4be96f9de79e2d440bd499fe0db"}, + {file = "pandas-2.2.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0cace394b6ea70c01ca1595f839cf193df35d1575986e484ad35c4aeae7266c1"}, + {file = "pandas-2.2.2-cp311-cp311-win_amd64.whl", hash = "sha256:873d13d177501a28b2756375d59816c365e42ed8417b41665f346289adc68d24"}, + {file = "pandas-2.2.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:9dfde2a0ddef507a631dc9dc4af6a9489d5e2e740e226ad426a05cabfbd7c8ef"}, + {file = "pandas-2.2.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e9b79011ff7a0f4b1d6da6a61aa1aa604fb312d6647de5bad20013682d1429ce"}, + {file = "pandas-2.2.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cb51fe389360f3b5a4d57dbd2848a5f033350336ca3b340d1c53a1fad33bcad"}, + {file = "pandas-2.2.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eee3a87076c0756de40b05c5e9a6069c035ba43e8dd71c379e68cab2c20f16ad"}, + {file = "pandas-2.2.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3e374f59e440d4ab45ca2fffde54b81ac3834cf5ae2cdfa69c90bc03bde04d76"}, + {file = "pandas-2.2.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:43498c0bdb43d55cb162cdc8c06fac328ccb5d2eabe3cadeb3529ae6f0517c32"}, + {file = "pandas-2.2.2-cp312-cp312-win_amd64.whl", hash = "sha256:d187d355ecec3629624fccb01d104da7d7f391db0311145817525281e2804d23"}, + {file = "pandas-2.2.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0ca6377b8fca51815f382bd0b697a0814c8bda55115678cbc94c30aacbb6eff2"}, + {file = "pandas-2.2.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9057e6aa78a584bc93a13f0a9bf7e753a5e9770a30b4d758b8d5f2a62a9433cd"}, + {file = "pandas-2.2.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:001910ad31abc7bf06f49dcc903755d2f7f3a9186c0c040b827e522e9cef0863"}, + {file = "pandas-2.2.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66b479b0bd07204e37583c191535505410daa8df638fd8e75ae1b383851fe921"}, + {file = "pandas-2.2.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a77e9d1c386196879aa5eb712e77461aaee433e54c68cf253053a73b7e49c33a"}, + {file = "pandas-2.2.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:92fd6b027924a7e178ac202cfbe25e53368db90d56872d20ffae94b96c7acc57"}, + {file = "pandas-2.2.2-cp39-cp39-win_amd64.whl", hash = "sha256:640cef9aa381b60e296db324337a554aeeb883ead99dc8f6c18e81a93942f5f4"}, + {file = "pandas-2.2.2.tar.gz", hash = "sha256:9e79019aba43cb4fda9e4d983f8e88ca0373adbb697ae9c6c43093218de28b54"}, +] + +[package.dependencies] +numpy = [ + {version = ">=1.22.4", markers = "python_version < \"3.11\""}, + {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, + {version = ">=1.23.2", markers = "python_version == \"3.11\""}, +] +python-dateutil = ">=2.8.2" +pytz = ">=2020.1" +tzdata = ">=2022.7" + +[package.extras] +all = ["PyQt5 (>=5.15.9)", "SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "adbc-driver-sqlite (>=0.8.0)", "beautifulsoup4 (>=4.11.2)", "bottleneck (>=1.3.6)", "dataframe-api-compat (>=0.1.7)", "fastparquet (>=2022.12.0)", "fsspec (>=2022.11.0)", "gcsfs (>=2022.11.0)", "html5lib (>=1.1)", "hypothesis (>=6.46.1)", "jinja2 (>=3.1.2)", "lxml (>=4.9.2)", "matplotlib (>=3.6.3)", "numba (>=0.56.4)", "numexpr (>=2.8.4)", "odfpy (>=1.4.1)", "openpyxl (>=3.1.0)", "pandas-gbq (>=0.19.0)", "psycopg2 (>=2.9.6)", "pyarrow (>=10.0.1)", "pymysql (>=1.0.2)", "pyreadstat (>=1.2.0)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)", "python-calamine (>=0.1.7)", "pyxlsb (>=1.0.10)", "qtpy (>=2.3.0)", "s3fs (>=2022.11.0)", "scipy (>=1.10.0)", "tables (>=3.8.0)", "tabulate (>=0.9.0)", "xarray (>=2022.12.0)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.5)", "zstandard (>=0.19.0)"] +aws = ["s3fs (>=2022.11.0)"] +clipboard = ["PyQt5 (>=5.15.9)", "qtpy (>=2.3.0)"] +compression = ["zstandard (>=0.19.0)"] +computation = ["scipy (>=1.10.0)", "xarray (>=2022.12.0)"] +consortium-standard = ["dataframe-api-compat (>=0.1.7)"] +excel = ["odfpy (>=1.4.1)", "openpyxl (>=3.1.0)", "python-calamine (>=0.1.7)", "pyxlsb (>=1.0.10)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.5)"] +feather = ["pyarrow (>=10.0.1)"] +fss = ["fsspec (>=2022.11.0)"] +gcp = ["gcsfs (>=2022.11.0)", "pandas-gbq (>=0.19.0)"] +hdf5 = ["tables (>=3.8.0)"] +html = ["beautifulsoup4 (>=4.11.2)", "html5lib (>=1.1)", "lxml (>=4.9.2)"] +mysql = ["SQLAlchemy (>=2.0.0)", "pymysql (>=1.0.2)"] +output-formatting = ["jinja2 (>=3.1.2)", "tabulate (>=0.9.0)"] +parquet = ["pyarrow (>=10.0.1)"] +performance = ["bottleneck (>=1.3.6)", "numba (>=0.56.4)", "numexpr (>=2.8.4)"] +plot = ["matplotlib (>=3.6.3)"] +postgresql = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "psycopg2 (>=2.9.6)"] +pyarrow = ["pyarrow (>=10.0.1)"] +spss = ["pyreadstat (>=1.2.0)"] +sql-other = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "adbc-driver-sqlite (>=0.8.0)"] +test = ["hypothesis (>=6.46.1)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)"] +xml = ["lxml (>=4.9.2)"] + +[[package]] +name = "parameterized" +version = "0.9.0" +description = "Parameterized testing with any Python test framework" +optional = false +python-versions = ">=3.7" +files = [ + {file = "parameterized-0.9.0-py2.py3-none-any.whl", hash = "sha256:4e0758e3d41bea3bbd05ec14fc2c24736723f243b28d702081aef438c9372b1b"}, + {file = "parameterized-0.9.0.tar.gz", hash = "sha256:7fc905272cefa4f364c1a3429cbbe9c0f98b793988efb5bf90aac80f08db09b1"}, +] + +[package.extras] +dev = ["jinja2"] + +[[package]] +name = "parso" +version = "0.8.4" +description = "A Python Parser" +optional = false +python-versions = ">=3.6" +files = [ + {file = "parso-0.8.4-py2.py3-none-any.whl", hash = "sha256:a418670a20291dacd2dddc80c377c5c3791378ee1e8d12bffc35420643d43f18"}, + {file = "parso-0.8.4.tar.gz", hash = "sha256:eb3a7b58240fb99099a345571deecc0f9540ea5f4dd2fe14c2a99d6b281ab92d"}, +] + +[package.extras] +qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"] +testing = ["docopt", "pytest"] + +[[package]] +name = "pathspec" +version = "0.12.1" +description = "Utility library for gitignore style pattern matching of file paths." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, + {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, +] + +[[package]] +name = "peft" +version = "0.12.0" +description = "Parameter-Efficient Fine-Tuning (PEFT)" +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "peft-0.12.0-py3-none-any.whl", hash = "sha256:a47915efb08af50e9fda267b7bf1b5b6eff33ccbb08791bdb544dccb8788f674"}, + {file = "peft-0.12.0.tar.gz", hash = "sha256:253205bd478e985ccdc7f04804aab9c95f479130c517bf6e474b8d509db5f4a4"}, +] + +[package.dependencies] +accelerate = ">=0.21.0" +huggingface-hub = ">=0.17.0" +numpy = ">=1.17" +packaging = ">=20.0" +psutil = "*" +pyyaml = "*" +safetensors = "*" +torch = ">=1.13.0" +tqdm = "*" +transformers = "*" + +[package.extras] +dev = ["black", "hf-doc-builder", "ruff (>=0.4.8,<0.5.0)"] +docs-specific = ["black", "hf-doc-builder"] +quality = ["black", "hf-doc-builder", "ruff (>=0.4.8,<0.5.0)"] +test = ["black", "datasets", "diffusers (<0.21.0)", "hf-doc-builder", "parameterized", "pytest", "pytest-cov", "pytest-xdist", "ruff (>=0.4.8,<0.5.0)", "scipy"] + +[[package]] +name = "pexpect" +version = "4.9.0" +description = "Pexpect allows easy control of interactive console applications." +optional = false +python-versions = "*" +files = [ + {file = "pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523"}, + {file = "pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f"}, +] + +[package.dependencies] +ptyprocess = ">=0.5" + +[[package]] +name = "pillow" +version = "10.4.0" +description = "Python Imaging Library (Fork)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pillow-10.4.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:4d9667937cfa347525b319ae34375c37b9ee6b525440f3ef48542fcf66f2731e"}, + {file = "pillow-10.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:543f3dc61c18dafb755773efc89aae60d06b6596a63914107f75459cf984164d"}, + {file = "pillow-10.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7928ecbf1ece13956b95d9cbcfc77137652b02763ba384d9ab508099a2eca856"}, + {file = "pillow-10.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4d49b85c4348ea0b31ea63bc75a9f3857869174e2bf17e7aba02945cd218e6f"}, + {file = "pillow-10.4.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:6c762a5b0997f5659a5ef2266abc1d8851ad7749ad9a6a5506eb23d314e4f46b"}, + {file = "pillow-10.4.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a985e028fc183bf12a77a8bbf36318db4238a3ded7fa9df1b9a133f1cb79f8fc"}, + {file = "pillow-10.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:812f7342b0eee081eaec84d91423d1b4650bb9828eb53d8511bcef8ce5aecf1e"}, + {file = "pillow-10.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ac1452d2fbe4978c2eec89fb5a23b8387aba707ac72810d9490118817d9c0b46"}, + {file = "pillow-10.4.0-cp310-cp310-win32.whl", hash = "sha256:bcd5e41a859bf2e84fdc42f4edb7d9aba0a13d29a2abadccafad99de3feff984"}, + {file = "pillow-10.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:ecd85a8d3e79cd7158dec1c9e5808e821feea088e2f69a974db5edf84dc53141"}, + {file = "pillow-10.4.0-cp310-cp310-win_arm64.whl", hash = "sha256:ff337c552345e95702c5fde3158acb0625111017d0e5f24bf3acdb9cc16b90d1"}, + {file = "pillow-10.4.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:0a9ec697746f268507404647e531e92889890a087e03681a3606d9b920fbee3c"}, + {file = "pillow-10.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dfe91cb65544a1321e631e696759491ae04a2ea11d36715eca01ce07284738be"}, + {file = "pillow-10.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5dc6761a6efc781e6a1544206f22c80c3af4c8cf461206d46a1e6006e4429ff3"}, + {file = "pillow-10.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e84b6cc6a4a3d76c153a6b19270b3526a5a8ed6b09501d3af891daa2a9de7d6"}, + {file = "pillow-10.4.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:bbc527b519bd3aa9d7f429d152fea69f9ad37c95f0b02aebddff592688998abe"}, + {file = "pillow-10.4.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:76a911dfe51a36041f2e756b00f96ed84677cdeb75d25c767f296c1c1eda1319"}, + {file = "pillow-10.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:59291fb29317122398786c2d44427bbd1a6d7ff54017075b22be9d21aa59bd8d"}, + {file = "pillow-10.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:416d3a5d0e8cfe4f27f574362435bc9bae57f679a7158e0096ad2beb427b8696"}, + {file = "pillow-10.4.0-cp311-cp311-win32.whl", hash = "sha256:7086cc1d5eebb91ad24ded9f58bec6c688e9f0ed7eb3dbbf1e4800280a896496"}, + {file = "pillow-10.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cbed61494057c0f83b83eb3a310f0bf774b09513307c434d4366ed64f4128a91"}, + {file = "pillow-10.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:f5f0c3e969c8f12dd2bb7e0b15d5c468b51e5017e01e2e867335c81903046a22"}, + {file = "pillow-10.4.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:673655af3eadf4df6b5457033f086e90299fdd7a47983a13827acf7459c15d94"}, + {file = "pillow-10.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:866b6942a92f56300012f5fbac71f2d610312ee65e22f1aa2609e491284e5597"}, + {file = "pillow-10.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29dbdc4207642ea6aad70fbde1a9338753d33fb23ed6956e706936706f52dd80"}, + {file = "pillow-10.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf2342ac639c4cf38799a44950bbc2dfcb685f052b9e262f446482afaf4bffca"}, + {file = "pillow-10.4.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:f5b92f4d70791b4a67157321c4e8225d60b119c5cc9aee8ecf153aace4aad4ef"}, + {file = "pillow-10.4.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:86dcb5a1eb778d8b25659d5e4341269e8590ad6b4e8b44d9f4b07f8d136c414a"}, + {file = "pillow-10.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:780c072c2e11c9b2c7ca37f9a2ee8ba66f44367ac3e5c7832afcfe5104fd6d1b"}, + {file = "pillow-10.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:37fb69d905be665f68f28a8bba3c6d3223c8efe1edf14cc4cfa06c241f8c81d9"}, + {file = "pillow-10.4.0-cp312-cp312-win32.whl", hash = "sha256:7dfecdbad5c301d7b5bde160150b4db4c659cee2b69589705b6f8a0c509d9f42"}, + {file = "pillow-10.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:1d846aea995ad352d4bdcc847535bd56e0fd88d36829d2c90be880ef1ee4668a"}, + {file = "pillow-10.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:e553cad5179a66ba15bb18b353a19020e73a7921296a7979c4a2b7f6a5cd57f9"}, + {file = "pillow-10.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8bc1a764ed8c957a2e9cacf97c8b2b053b70307cf2996aafd70e91a082e70df3"}, + {file = "pillow-10.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6209bb41dc692ddfee4942517c19ee81b86c864b626dbfca272ec0f7cff5d9fb"}, + {file = "pillow-10.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bee197b30783295d2eb680b311af15a20a8b24024a19c3a26431ff83eb8d1f70"}, + {file = "pillow-10.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ef61f5dd14c300786318482456481463b9d6b91ebe5ef12f405afbba77ed0be"}, + {file = "pillow-10.4.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:297e388da6e248c98bc4a02e018966af0c5f92dfacf5a5ca22fa01cb3179bca0"}, + {file = "pillow-10.4.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:e4db64794ccdf6cb83a59d73405f63adbe2a1887012e308828596100a0b2f6cc"}, + {file = "pillow-10.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bd2880a07482090a3bcb01f4265f1936a903d70bc740bfcb1fd4e8a2ffe5cf5a"}, + {file = "pillow-10.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4b35b21b819ac1dbd1233317adeecd63495f6babf21b7b2512d244ff6c6ce309"}, + {file = "pillow-10.4.0-cp313-cp313-win32.whl", hash = "sha256:551d3fd6e9dc15e4c1eb6fc4ba2b39c0c7933fa113b220057a34f4bb3268a060"}, + {file = "pillow-10.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:030abdbe43ee02e0de642aee345efa443740aa4d828bfe8e2eb11922ea6a21ea"}, + {file = "pillow-10.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:5b001114dd152cfd6b23befeb28d7aee43553e2402c9f159807bf55f33af8a8d"}, + {file = "pillow-10.4.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:8d4d5063501b6dd4024b8ac2f04962d661222d120381272deea52e3fc52d3736"}, + {file = "pillow-10.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7c1ee6f42250df403c5f103cbd2768a28fe1a0ea1f0f03fe151c8741e1469c8b"}, + {file = "pillow-10.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b15e02e9bb4c21e39876698abf233c8c579127986f8207200bc8a8f6bb27acf2"}, + {file = "pillow-10.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a8d4bade9952ea9a77d0c3e49cbd8b2890a399422258a77f357b9cc9be8d680"}, + {file = "pillow-10.4.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:43efea75eb06b95d1631cb784aa40156177bf9dd5b4b03ff38979e048258bc6b"}, + {file = "pillow-10.4.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:950be4d8ba92aca4b2bb0741285a46bfae3ca699ef913ec8416c1b78eadd64cd"}, + {file = "pillow-10.4.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d7480af14364494365e89d6fddc510a13e5a2c3584cb19ef65415ca57252fb84"}, + {file = "pillow-10.4.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:73664fe514b34c8f02452ffb73b7a92c6774e39a647087f83d67f010eb9a0cf0"}, + {file = "pillow-10.4.0-cp38-cp38-win32.whl", hash = "sha256:e88d5e6ad0d026fba7bdab8c3f225a69f063f116462c49892b0149e21b6c0a0e"}, + {file = "pillow-10.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:5161eef006d335e46895297f642341111945e2c1c899eb406882a6c61a4357ab"}, + {file = "pillow-10.4.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:0ae24a547e8b711ccaaf99c9ae3cd975470e1a30caa80a6aaee9a2f19c05701d"}, + {file = "pillow-10.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:298478fe4f77a4408895605f3482b6cc6222c018b2ce565c2b6b9c354ac3229b"}, + {file = "pillow-10.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:134ace6dc392116566980ee7436477d844520a26a4b1bd4053f6f47d096997fd"}, + {file = "pillow-10.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:930044bb7679ab003b14023138b50181899da3f25de50e9dbee23b61b4de2126"}, + {file = "pillow-10.4.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:c76e5786951e72ed3686e122d14c5d7012f16c8303a674d18cdcd6d89557fc5b"}, + {file = "pillow-10.4.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:b2724fdb354a868ddf9a880cb84d102da914e99119211ef7ecbdc613b8c96b3c"}, + {file = "pillow-10.4.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:dbc6ae66518ab3c5847659e9988c3b60dc94ffb48ef9168656e0019a93dbf8a1"}, + {file = "pillow-10.4.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:06b2f7898047ae93fad74467ec3d28fe84f7831370e3c258afa533f81ef7f3df"}, + {file = "pillow-10.4.0-cp39-cp39-win32.whl", hash = "sha256:7970285ab628a3779aecc35823296a7869f889b8329c16ad5a71e4901a3dc4ef"}, + {file = "pillow-10.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:961a7293b2457b405967af9c77dcaa43cc1a8cd50d23c532e62d48ab6cdd56f5"}, + {file = "pillow-10.4.0-cp39-cp39-win_arm64.whl", hash = "sha256:32cda9e3d601a52baccb2856b8ea1fc213c90b340c542dcef77140dfa3278a9e"}, + {file = "pillow-10.4.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5b4815f2e65b30f5fbae9dfffa8636d992d49705723fe86a3661806e069352d4"}, + {file = "pillow-10.4.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:8f0aef4ef59694b12cadee839e2ba6afeab89c0f39a3adc02ed51d109117b8da"}, + {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f4727572e2918acaa9077c919cbbeb73bd2b3ebcfe033b72f858fc9fbef0026"}, + {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff25afb18123cea58a591ea0244b92eb1e61a1fd497bf6d6384f09bc3262ec3e"}, + {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:dc3e2db6ba09ffd7d02ae9141cfa0ae23393ee7687248d46a7507b75d610f4f5"}, + {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:02a2be69f9c9b8c1e97cf2713e789d4e398c751ecfd9967c18d0ce304efbf885"}, + {file = "pillow-10.4.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:0755ffd4a0c6f267cccbae2e9903d95477ca2f77c4fcf3a3a09570001856c8a5"}, + {file = "pillow-10.4.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:a02364621fe369e06200d4a16558e056fe2805d3468350df3aef21e00d26214b"}, + {file = "pillow-10.4.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:1b5dea9831a90e9d0721ec417a80d4cbd7022093ac38a568db2dd78363b00908"}, + {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b885f89040bb8c4a1573566bbb2f44f5c505ef6e74cec7ab9068c900047f04b"}, + {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87dd88ded2e6d74d31e1e0a99a726a6765cda32d00ba72dc37f0651f306daaa8"}, + {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:2db98790afc70118bd0255c2eeb465e9767ecf1f3c25f9a1abb8ffc8cfd1fe0a"}, + {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f7baece4ce06bade126fb84b8af1c33439a76d8a6fd818970215e0560ca28c27"}, + {file = "pillow-10.4.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:cfdd747216947628af7b259d274771d84db2268ca062dd5faf373639d00113a3"}, + {file = "pillow-10.4.0.tar.gz", hash = "sha256:166c1cd4d24309b30d61f79f4a9114b7b2313d7450912277855ff5dfd7cd4a06"}, +] + +[package.extras] +docs = ["furo", "olefile", "sphinx (>=7.3)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinxext-opengraph"] +fpx = ["olefile"] +mic = ["olefile"] +tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"] +typing = ["typing-extensions"] +xmp = ["defusedxml"] + +[[package]] +name = "pip" +version = "24.2" +description = "The PyPA recommended tool for installing Python packages." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pip-24.2-py3-none-any.whl", hash = "sha256:2cd581cf58ab7fcfca4ce8efa6dcacd0de5bf8d0a3eb9ec927e07405f4d9e2a2"}, + {file = "pip-24.2.tar.gz", hash = "sha256:5b5e490b5e9cb275c879595064adce9ebd31b854e3e803740b72f9ccf34a45b8"}, +] + +[[package]] +name = "pip-api" +version = "0.0.34" +description = "An unofficial, importable pip API" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pip_api-0.0.34-py3-none-any.whl", hash = "sha256:8b2d7d7c37f2447373aa2cf8b1f60a2f2b27a84e1e9e0294a3f6ef10eb3ba6bb"}, + {file = "pip_api-0.0.34.tar.gz", hash = "sha256:9b75e958f14c5a2614bae415f2adf7eeb54d50a2cfbe7e24fd4826471bac3625"}, +] + +[package.dependencies] +pip = "*" + +[[package]] +name = "pip-audit" +version = "2.7.3" +description = "A tool for scanning Python environments for known vulnerabilities" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pip_audit-2.7.3-py3-none-any.whl", hash = "sha256:46a11faee3323f76adf7987de8171daeb660e8f57d8088cc27fb1c1e5c7747b0"}, + {file = "pip_audit-2.7.3.tar.gz", hash = "sha256:08891bbf179bffe478521f150818112bae998424f58bf9285c0078965aef38bc"}, +] + +[package.dependencies] +CacheControl = {version = ">=0.13.0", extras = ["filecache"]} +cyclonedx-python-lib = ">=5,<8" +html5lib = ">=1.1" +packaging = ">=23.0.0" +pip-api = ">=0.0.28" +pip-requirements-parser = ">=32.0.0" +requests = ">=2.31.0" +rich = ">=12.4" +toml = ">=0.10" + +[package.extras] +dev = ["build", "bump (>=1.3.2)", "pip-audit[doc,lint,test]"] +doc = ["pdoc"] +lint = ["interrogate", "mypy", "ruff (<0.4.3)", "setuptools", "types-html5lib", "types-requests", "types-toml"] +test = ["coverage[toml] (>=7.0,!=7.3.3,<8.0)", "pretend", "pytest", "pytest-cov"] + +[[package]] +name = "pip-requirements-parser" +version = "32.0.1" +description = "pip requirements parser - a mostly correct pip requirements parsing library because it uses pip's own code." +optional = false +python-versions = ">=3.6.0" +files = [ + {file = "pip-requirements-parser-32.0.1.tar.gz", hash = "sha256:b4fa3a7a0be38243123cf9d1f3518da10c51bdb165a2b2985566247f9155a7d3"}, + {file = "pip_requirements_parser-32.0.1-py3-none-any.whl", hash = "sha256:4659bc2a667783e7a15d190f6fccf8b2486685b6dba4c19c3876314769c57526"}, +] + +[package.dependencies] +packaging = "*" +pyparsing = "*" + +[package.extras] +docs = ["Sphinx (>=3.3.1)", "doc8 (>=0.8.1)", "sphinx-rtd-theme (>=0.5.0)"] +testing = ["aboutcode-toolkit (>=6.0.0)", "black", "pytest (>=6,!=7.0.0)", "pytest-xdist (>=2)"] + +[[package]] +name = "platformdirs" +version = "4.2.2" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." +optional = false +python-versions = ">=3.8" +files = [ + {file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"}, + {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"}, +] + +[package.extras] +docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] +type = ["mypy (>=1.8)"] + +[[package]] +name = "pluggy" +version = "1.5.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, + {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "portalocker" +version = "2.10.1" +description = "Wraps the portalocker recipe for easy usage" +optional = false +python-versions = ">=3.8" +files = [ + {file = "portalocker-2.10.1-py3-none-any.whl", hash = "sha256:53a5984ebc86a025552264b459b46a2086e269b21823cb572f8f28ee759e45bf"}, + {file = "portalocker-2.10.1.tar.gz", hash = "sha256:ef1bf844e878ab08aee7e40184156e1151f228f103aa5c6bd0724cc330960f8f"}, +] + +[package.dependencies] +pywin32 = {version = ">=226", markers = "platform_system == \"Windows\""} + +[package.extras] +docs = ["sphinx (>=1.7.1)"] +redis = ["redis"] +tests = ["pytest (>=5.4.1)", "pytest-cov (>=2.8.1)", "pytest-mypy (>=0.8.0)", "pytest-timeout (>=2.1.0)", "redis", "sphinx (>=6.0.0)", "types-redis"] + +[[package]] +name = "pre-commit" +version = "3.8.0" +description = "A framework for managing and maintaining multi-language pre-commit hooks." +optional = false +python-versions = ">=3.9" +files = [ + {file = "pre_commit-3.8.0-py2.py3-none-any.whl", hash = "sha256:9a90a53bf82fdd8778d58085faf8d83df56e40dfe18f45b19446e26bf1b3a63f"}, + {file = "pre_commit-3.8.0.tar.gz", hash = "sha256:8bb6494d4a20423842e198980c9ecf9f96607a07ea29549e180eef9ae80fe7af"}, +] + +[package.dependencies] +cfgv = ">=2.0.0" +identify = ">=1.0.0" +nodeenv = ">=0.11.1" +pyyaml = ">=5.1" +virtualenv = ">=20.10.0" + +[[package]] +name = "prompt-toolkit" +version = "3.0.47" +description = "Library for building powerful interactive command lines in Python" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "prompt_toolkit-3.0.47-py3-none-any.whl", hash = "sha256:0d7bfa67001d5e39d02c224b663abc33687405033a8c422d0d675a5a13361d10"}, + {file = "prompt_toolkit-3.0.47.tar.gz", hash = "sha256:1e1b29cb58080b1e69f207c893a1a7bf16d127a5c30c9d17a25a5d77792e5360"}, +] + +[package.dependencies] +wcwidth = "*" + +[[package]] +name = "protobuf" +version = "5.27.3" +description = "" +optional = false +python-versions = ">=3.8" +files = [ + {file = "protobuf-5.27.3-cp310-abi3-win32.whl", hash = "sha256:dcb307cd4ef8fec0cf52cb9105a03d06fbb5275ce6d84a6ae33bc6cf84e0a07b"}, + {file = "protobuf-5.27.3-cp310-abi3-win_amd64.whl", hash = "sha256:16ddf3f8c6c41e1e803da7abea17b1793a97ef079a912e42351eabb19b2cffe7"}, + {file = "protobuf-5.27.3-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:68248c60d53f6168f565a8c76dc58ba4fa2ade31c2d1ebdae6d80f969cdc2d4f"}, + {file = "protobuf-5.27.3-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:b8a994fb3d1c11156e7d1e427186662b64694a62b55936b2b9348f0a7c6625ce"}, + {file = "protobuf-5.27.3-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:a55c48f2a2092d8e213bd143474df33a6ae751b781dd1d1f4d953c128a415b25"}, + {file = "protobuf-5.27.3-cp38-cp38-win32.whl", hash = "sha256:043853dcb55cc262bf2e116215ad43fa0859caab79bb0b2d31b708f128ece035"}, + {file = "protobuf-5.27.3-cp38-cp38-win_amd64.whl", hash = "sha256:c2a105c24f08b1e53d6c7ffe69cb09d0031512f0b72f812dd4005b8112dbe91e"}, + {file = "protobuf-5.27.3-cp39-cp39-win32.whl", hash = "sha256:c84eee2c71ed83704f1afbf1a85c3171eab0fd1ade3b399b3fad0884cbcca8bf"}, + {file = "protobuf-5.27.3-cp39-cp39-win_amd64.whl", hash = "sha256:af7c0b7cfbbb649ad26132e53faa348580f844d9ca46fd3ec7ca48a1ea5db8a1"}, + {file = "protobuf-5.27.3-py3-none-any.whl", hash = "sha256:8572c6533e544ebf6899c360e91d6bcbbee2549251643d32c52cf8a5de295ba5"}, + {file = "protobuf-5.27.3.tar.gz", hash = "sha256:82460903e640f2b7e34ee81a947fdaad89de796d324bcbc38ff5430bcdead82c"}, +] + +[[package]] +name = "psutil" +version = "6.0.0" +description = "Cross-platform lib for process and system monitoring in Python." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +files = [ + {file = "psutil-6.0.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a021da3e881cd935e64a3d0a20983bda0bb4cf80e4f74fa9bfcb1bc5785360c6"}, + {file = "psutil-6.0.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:1287c2b95f1c0a364d23bc6f2ea2365a8d4d9b726a3be7294296ff7ba97c17f0"}, + {file = "psutil-6.0.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:a9a3dbfb4de4f18174528d87cc352d1f788b7496991cca33c6996f40c9e3c92c"}, + {file = "psutil-6.0.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:6ec7588fb3ddaec7344a825afe298db83fe01bfaaab39155fa84cf1c0d6b13c3"}, + {file = "psutil-6.0.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:1e7c870afcb7d91fdea2b37c24aeb08f98b6d67257a5cb0a8bc3ac68d0f1a68c"}, + {file = "psutil-6.0.0-cp27-none-win32.whl", hash = "sha256:02b69001f44cc73c1c5279d02b30a817e339ceb258ad75997325e0e6169d8b35"}, + {file = "psutil-6.0.0-cp27-none-win_amd64.whl", hash = "sha256:21f1fb635deccd510f69f485b87433460a603919b45e2a324ad65b0cc74f8fb1"}, + {file = "psutil-6.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:c588a7e9b1173b6e866756dde596fd4cad94f9399daf99ad8c3258b3cb2b47a0"}, + {file = "psutil-6.0.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ed2440ada7ef7d0d608f20ad89a04ec47d2d3ab7190896cd62ca5fc4fe08bf0"}, + {file = "psutil-6.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fd9a97c8e94059b0ef54a7d4baf13b405011176c3b6ff257c247cae0d560ecd"}, + {file = "psutil-6.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2e8d0054fc88153ca0544f5c4d554d42e33df2e009c4ff42284ac9ebdef4132"}, + {file = "psutil-6.0.0-cp36-cp36m-win32.whl", hash = "sha256:fc8c9510cde0146432bbdb433322861ee8c3efbf8589865c8bf8d21cb30c4d14"}, + {file = "psutil-6.0.0-cp36-cp36m-win_amd64.whl", hash = "sha256:34859b8d8f423b86e4385ff3665d3f4d94be3cdf48221fbe476e883514fdb71c"}, + {file = "psutil-6.0.0-cp37-abi3-win32.whl", hash = "sha256:a495580d6bae27291324fe60cea0b5a7c23fa36a7cd35035a16d93bdcf076b9d"}, + {file = "psutil-6.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:33ea5e1c975250a720b3a6609c490db40dae5d83a4eb315170c4fe0d8b1f34b3"}, + {file = "psutil-6.0.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:ffe7fc9b6b36beadc8c322f84e1caff51e8703b88eee1da46d1e3a6ae11b4fd0"}, + {file = "psutil-6.0.0.tar.gz", hash = "sha256:8faae4f310b6d969fa26ca0545338b21f73c6b15db7c4a8d934a5482faa818f2"}, +] + +[package.extras] +test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"] + +[[package]] +name = "ptyprocess" +version = "0.7.0" +description = "Run a subprocess in a pseudo terminal" +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.3" +description = "Safely evaluate AST nodes without side effects" +optional = false +python-versions = "*" +files = [ + {file = "pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0"}, + {file = "pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42"}, +] + +[package.extras] +tests = ["pytest"] + +[[package]] +name = "py-serializable" +version = "1.1.0" +description = "Library for serializing and deserializing Python Objects to and from JSON and XML." +optional = false +python-versions = "<4.0,>=3.8" +files = [ + {file = "py_serializable-1.1.0-py3-none-any.whl", hash = "sha256:ae7ae4326b0d037b7e710f6e8bb1a97ece4ac2895a1f443a17ffd17f85547d76"}, + {file = "py_serializable-1.1.0.tar.gz", hash = "sha256:3311ab39063b131caca0fb75e2038153682e55576c67f24a2de72d402dccb6e0"}, +] + +[package.dependencies] +defusedxml = ">=0.7.1,<0.8.0" + +[[package]] +name = "pycodestyle" +version = "2.12.1" +description = "Python style guide checker" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pycodestyle-2.12.1-py2.py3-none-any.whl", hash = "sha256:46f0fb92069a7c28ab7bb558f05bfc0110dac69a0cd23c61ea0040283a9d78b3"}, + {file = "pycodestyle-2.12.1.tar.gz", hash = "sha256:6838eae08bbce4f6accd5d5572075c63626a15ee3e6f842df996bf62f6d73521"}, +] + +[[package]] +name = "pycparser" +version = "2.22" +description = "C parser in Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, + {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, +] + +[[package]] +name = "pyflakes" +version = "3.2.0" +description = "passive checker of Python programs" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pyflakes-3.2.0-py2.py3-none-any.whl", hash = "sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a"}, + {file = "pyflakes-3.2.0.tar.gz", hash = "sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f"}, +] + +[[package]] +name = "pygments" +version = "2.18.0" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"}, + {file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"}, +] + +[package.extras] +windows-terminal = ["colorama (>=0.4.6)"] + +[[package]] +name = "pylint" +version = "3.2.6" +description = "python code static checker" +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "pylint-3.2.6-py3-none-any.whl", hash = "sha256:03c8e3baa1d9fb995b12c1dbe00aa6c4bcef210c2a2634374aedeb22fb4a8f8f"}, + {file = "pylint-3.2.6.tar.gz", hash = "sha256:a5d01678349454806cff6d886fb072294f56a58c4761278c97fb557d708e1eb3"}, +] + +[package.dependencies] +astroid = ">=3.2.4,<=3.3.0-dev0" +colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} +dill = [ + {version = ">=0.2", markers = "python_version < \"3.11\""}, + {version = ">=0.3.7", markers = "python_version >= \"3.12\""}, + {version = ">=0.3.6", markers = "python_version >= \"3.11\" and python_version < \"3.12\""}, +] +isort = ">=4.2.5,<5.13.0 || >5.13.0,<6" +mccabe = ">=0.6,<0.8" +platformdirs = ">=2.2.0" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +tomlkit = ">=0.10.1" +typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""} + +[package.extras] +spelling = ["pyenchant (>=3.2,<4.0)"] +testutils = ["gitpython (>3)"] + +[[package]] +name = "pyparsing" +version = "3.1.2" +description = "pyparsing module - Classes and methods to define and execute parsing grammars" +optional = false +python-versions = ">=3.6.8" +files = [ + {file = "pyparsing-3.1.2-py3-none-any.whl", hash = "sha256:f9db75911801ed778fe61bb643079ff86601aca99fcae6345aa67292038fb742"}, + {file = "pyparsing-3.1.2.tar.gz", hash = "sha256:a1bac0ce561155ecc3ed78ca94d3c9378656ad4c94c1270de543f621420f94ad"}, +] + +[package.extras] +diagrams = ["jinja2", "railroad-diagrams"] + +[[package]] +name = "pytest" +version = "8.3.2" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-8.3.2-py3-none-any.whl", hash = "sha256:4ba08f9ae7dcf84ded419494d229b48d0903ea6407b030eaec46df5e6a73bba5"}, + {file = "pytest-8.3.2.tar.gz", hash = "sha256:c132345d12ce551242c87269de812483f5bcc87cdbb4722e48487ba194f9fdce"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=1.5,<2" +tomli = {version = ">=1", markers = "python_version < \"3.11\""} + +[package.extras] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +description = "Extensions to the standard Python datetime module" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "pytorch-lightning" +version = "2.4.0" +description = "PyTorch Lightning is the lightweight PyTorch wrapper for ML researchers. Scale your models. Write less boilerplate." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytorch-lightning-2.4.0.tar.gz", hash = "sha256:6aa897fd9d6dfa7b7b49f37c2f04e13592861831d08deae584dfda423fdb71c8"}, + {file = "pytorch_lightning-2.4.0-py3-none-any.whl", hash = "sha256:9ac7935229ac022ef06994c928217ed37f525ac6700f7d4fc57009624570e655"}, +] + +[package.dependencies] +fsspec = {version = ">=2022.5.0", extras = ["http"]} +lightning-utilities = ">=0.10.0" +packaging = ">=20.0" +PyYAML = ">=5.4" +torch = ">=2.1.0" +torchmetrics = ">=0.7.0" +tqdm = ">=4.57.0" +typing-extensions = ">=4.4.0" + +[package.extras] +all = ["bitsandbytes (>=0.42.0)", "deepspeed (>=0.8.2,<=0.9.3)", "hydra-core (>=1.2.0)", "ipython[all] (<8.15.0)", "jsonargparse[signatures] (>=4.27.7)", "lightning-utilities (>=0.8.0)", "matplotlib (>3.1)", "omegaconf (>=2.2.3)", "requests (<2.32.0)", "rich (>=12.3.0)", "tensorboardX (>=2.2)", "torchmetrics (>=0.10.0)", "torchvision (>=0.16.0)"] +deepspeed = ["deepspeed (>=0.8.2,<=0.9.3)"] +dev = ["bitsandbytes (>=0.42.0)", "cloudpickle (>=1.3)", "coverage (==7.3.1)", "deepspeed (>=0.8.2,<=0.9.3)", "fastapi", "hydra-core (>=1.2.0)", "ipython[all] (<8.15.0)", "jsonargparse[signatures] (>=4.27.7)", "lightning-utilities (>=0.8.0)", "matplotlib (>3.1)", "numpy (>=1.17.2)", "omegaconf (>=2.2.3)", "onnx (>=1.12.0)", "onnxruntime (>=1.12.0)", "pandas (>1.0)", "psutil (<5.9.6)", "pytest (==7.4.0)", "pytest-cov (==4.1.0)", "pytest-random-order (==1.1.0)", "pytest-rerunfailures (==12.0)", "pytest-timeout (==2.1.0)", "requests (<2.32.0)", "rich (>=12.3.0)", "scikit-learn (>0.22.1)", "tensorboard (>=2.9.1)", "tensorboardX (>=2.2)", "torchmetrics (>=0.10.0)", "torchvision (>=0.16.0)", "uvicorn"] +examples = ["ipython[all] (<8.15.0)", "lightning-utilities (>=0.8.0)", "requests (<2.32.0)", "torchmetrics (>=0.10.0)", "torchvision (>=0.16.0)"] +extra = ["bitsandbytes (>=0.42.0)", "hydra-core (>=1.2.0)", "jsonargparse[signatures] (>=4.27.7)", "matplotlib (>3.1)", "omegaconf (>=2.2.3)", "rich (>=12.3.0)", "tensorboardX (>=2.2)"] +strategies = ["deepspeed (>=0.8.2,<=0.9.3)"] +test = ["cloudpickle (>=1.3)", "coverage (==7.3.1)", "fastapi", "numpy (>=1.17.2)", "onnx (>=1.12.0)", "onnxruntime (>=1.12.0)", "pandas (>1.0)", "psutil (<5.9.6)", "pytest (==7.4.0)", "pytest-cov (==4.1.0)", "pytest-random-order (==1.1.0)", "pytest-rerunfailures (==12.0)", "pytest-timeout (==2.1.0)", "scikit-learn (>0.22.1)", "tensorboard (>=2.9.1)", "uvicorn"] + +[[package]] +name = "pytorchvideo" +version = "0.1.5" +description = "A video understanding deep learning library." +optional = false +python-versions = ">=3.7" +files = [] +develop = false + +[package.dependencies] +av = "*" +fvcore = "*" +iopath = "*" +networkx = "*" +parameterized = "*" + +[package.extras] +dev = ["autoflake (==1.4)", "black (==20.8b1)", "bs4", "decord", "flake8 (==3.8.1)", "flake8-bugbear", "flake8-comprehensions", "isort (==4.3.21)", "nbconvert", "opencv-python", "pre-commit", "sphinx"] +opencv-python = ["opencv-python"] +test = ["coverage", "decord", "opencv-python", "pytest"] + +[package.source] +type = "git" +url = "https://github.com/facebookresearch/pytorchvideo" +reference = "main" +resolved_reference = "1fadaef40dd393ca09680f55582399f4679fc9b7" + +[[package]] +name = "pytz" +version = "2024.1" +description = "World timezone definitions, modern and historical" +optional = false +python-versions = "*" +files = [ + {file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"}, + {file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"}, +] + +[[package]] +name = "pyupgrade" +version = "3.17.0" +description = "A tool to automatically upgrade syntax for newer versions." +optional = false +python-versions = ">=3.9" +files = [ + {file = "pyupgrade-3.17.0-py2.py3-none-any.whl", hash = "sha256:cbc8f67a61d3f4e7ca9c2ef57b9aae67f023d3780ce30c99fccec78401723754"}, + {file = "pyupgrade-3.17.0.tar.gz", hash = "sha256:d5dd1dcaf9a016c31508bb9d3d09fd335d736578092f91df52bb26ac30c37919"}, +] + +[package.dependencies] +tokenize-rt = ">=5.2.0" + +[[package]] +name = "pywin32" +version = "306" +description = "Python for Window Extensions" +optional = false +python-versions = "*" +files = [ + {file = "pywin32-306-cp310-cp310-win32.whl", hash = "sha256:06d3420a5155ba65f0b72f2699b5bacf3109f36acbe8923765c22938a69dfc8d"}, + {file = "pywin32-306-cp310-cp310-win_amd64.whl", hash = "sha256:84f4471dbca1887ea3803d8848a1616429ac94a4a8d05f4bc9c5dcfd42ca99c8"}, + {file = "pywin32-306-cp311-cp311-win32.whl", hash = "sha256:e65028133d15b64d2ed8f06dd9fbc268352478d4f9289e69c190ecd6818b6407"}, + {file = "pywin32-306-cp311-cp311-win_amd64.whl", hash = "sha256:a7639f51c184c0272e93f244eb24dafca9b1855707d94c192d4a0b4c01e1100e"}, + {file = "pywin32-306-cp311-cp311-win_arm64.whl", hash = "sha256:70dba0c913d19f942a2db25217d9a1b726c278f483a919f1abfed79c9cf64d3a"}, + {file = "pywin32-306-cp312-cp312-win32.whl", hash = "sha256:383229d515657f4e3ed1343da8be101000562bf514591ff383ae940cad65458b"}, + {file = "pywin32-306-cp312-cp312-win_amd64.whl", hash = "sha256:37257794c1ad39ee9be652da0462dc2e394c8159dfd913a8a4e8eb6fd346da0e"}, + {file = "pywin32-306-cp312-cp312-win_arm64.whl", hash = "sha256:5821ec52f6d321aa59e2db7e0a35b997de60c201943557d108af9d4ae1ec7040"}, + {file = "pywin32-306-cp37-cp37m-win32.whl", hash = "sha256:1c73ea9a0d2283d889001998059f5eaaba3b6238f767c9cf2833b13e6a685f65"}, + {file = "pywin32-306-cp37-cp37m-win_amd64.whl", hash = "sha256:72c5f621542d7bdd4fdb716227be0dd3f8565c11b280be6315b06ace35487d36"}, + {file = "pywin32-306-cp38-cp38-win32.whl", hash = "sha256:e4c092e2589b5cf0d365849e73e02c391c1349958c5ac3e9d5ccb9a28e017b3a"}, + {file = "pywin32-306-cp38-cp38-win_amd64.whl", hash = "sha256:e8ac1ae3601bee6ca9f7cb4b5363bf1c0badb935ef243c4733ff9a393b1690c0"}, + {file = "pywin32-306-cp39-cp39-win32.whl", hash = "sha256:e25fd5b485b55ac9c057f67d94bc203f3f6595078d1fb3b458c9c28b7153a802"}, + {file = "pywin32-306-cp39-cp39-win_amd64.whl", hash = "sha256:39b61c15272833b5c329a2989999dcae836b1eed650252ab1b7bfbe1d59f30f4"}, +] + +[[package]] +name = "pyyaml" +version = "6.0.2" +description = "YAML parser and emitter for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, + {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"}, + {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"}, + {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"}, + {file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"}, + {file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"}, + {file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"}, + {file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"}, + {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"}, + {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"}, + {file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"}, + {file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"}, + {file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"}, + {file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"}, + {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"}, + {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"}, + {file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"}, + {file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"}, + {file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"}, + {file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"}, + {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"}, + {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"}, + {file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"}, + {file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"}, + {file = "PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083"}, + {file = "PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706"}, + {file = "PyYAML-6.0.2-cp38-cp38-win32.whl", hash = "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a"}, + {file = "PyYAML-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff"}, + {file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"}, + {file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"}, + {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"}, + {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"}, + {file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"}, + {file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"}, + {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, +] + +[[package]] +name = "pyzmq" +version = "26.1.0" +description = "Python bindings for 0MQ" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pyzmq-26.1.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:263cf1e36862310bf5becfbc488e18d5d698941858860c5a8c079d1511b3b18e"}, + {file = "pyzmq-26.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d5c8b17f6e8f29138678834cf8518049e740385eb2dbf736e8f07fc6587ec682"}, + {file = "pyzmq-26.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:75a95c2358fcfdef3374cb8baf57f1064d73246d55e41683aaffb6cfe6862917"}, + {file = "pyzmq-26.1.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f99de52b8fbdb2a8f5301ae5fc0f9e6b3ba30d1d5fc0421956967edcc6914242"}, + {file = "pyzmq-26.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7bcbfbab4e1895d58ab7da1b5ce9a327764f0366911ba5b95406c9104bceacb0"}, + {file = "pyzmq-26.1.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:77ce6a332c7e362cb59b63f5edf730e83590d0ab4e59c2aa5bd79419a42e3449"}, + {file = "pyzmq-26.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ba0a31d00e8616149a5ab440d058ec2da621e05d744914774c4dde6837e1f545"}, + {file = "pyzmq-26.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8b88641384e84a258b740801cd4dbc45c75f148ee674bec3149999adda4a8598"}, + {file = "pyzmq-26.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2fa76ebcebe555cce90f16246edc3ad83ab65bb7b3d4ce408cf6bc67740c4f88"}, + {file = "pyzmq-26.1.0-cp310-cp310-win32.whl", hash = "sha256:fbf558551cf415586e91160d69ca6416f3fce0b86175b64e4293644a7416b81b"}, + {file = "pyzmq-26.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:a7b8aab50e5a288c9724d260feae25eda69582be84e97c012c80e1a5e7e03fb2"}, + {file = "pyzmq-26.1.0-cp310-cp310-win_arm64.whl", hash = "sha256:08f74904cb066e1178c1ec706dfdb5c6c680cd7a8ed9efebeac923d84c1f13b1"}, + {file = "pyzmq-26.1.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:46d6800b45015f96b9d92ece229d92f2aef137d82906577d55fadeb9cf5fcb71"}, + {file = "pyzmq-26.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5bc2431167adc50ba42ea3e5e5f5cd70d93e18ab7b2f95e724dd8e1bd2c38120"}, + {file = "pyzmq-26.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b3bb34bebaa1b78e562931a1687ff663d298013f78f972a534f36c523311a84d"}, + {file = "pyzmq-26.1.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd3f6329340cef1c7ba9611bd038f2d523cea79f09f9c8f6b0553caba59ec562"}, + {file = "pyzmq-26.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:471880c4c14e5a056a96cd224f5e71211997d40b4bf5e9fdded55dafab1f98f2"}, + {file = "pyzmq-26.1.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:ce6f2b66799971cbae5d6547acefa7231458289e0ad481d0be0740535da38d8b"}, + {file = "pyzmq-26.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0a1f6ea5b1d6cdbb8cfa0536f0d470f12b4b41ad83625012e575f0e3ecfe97f0"}, + {file = "pyzmq-26.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:b45e6445ac95ecb7d728604bae6538f40ccf4449b132b5428c09918523abc96d"}, + {file = "pyzmq-26.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:94c4262626424683feea0f3c34951d39d49d354722db2745c42aa6bb50ecd93b"}, + {file = "pyzmq-26.1.0-cp311-cp311-win32.whl", hash = "sha256:a0f0ab9df66eb34d58205913f4540e2ad17a175b05d81b0b7197bc57d000e829"}, + {file = "pyzmq-26.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:8efb782f5a6c450589dbab4cb0f66f3a9026286333fe8f3a084399149af52f29"}, + {file = "pyzmq-26.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:f133d05aaf623519f45e16ab77526e1e70d4e1308e084c2fb4cedb1a0c764bbb"}, + {file = "pyzmq-26.1.0-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:3d3146b1c3dcc8a1539e7cc094700b2be1e605a76f7c8f0979b6d3bde5ad4072"}, + {file = "pyzmq-26.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d9270fbf038bf34ffca4855bcda6e082e2c7f906b9eb8d9a8ce82691166060f7"}, + {file = "pyzmq-26.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:995301f6740a421afc863a713fe62c0aaf564708d4aa057dfdf0f0f56525294b"}, + {file = "pyzmq-26.1.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7eca8b89e56fb8c6c26dd3e09bd41b24789022acf1cf13358e96f1cafd8cae3"}, + {file = "pyzmq-26.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d4feb2e83dfe9ace6374a847e98ee9d1246ebadcc0cb765482e272c34e5820"}, + {file = "pyzmq-26.1.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:d4fafc2eb5d83f4647331267808c7e0c5722c25a729a614dc2b90479cafa78bd"}, + {file = "pyzmq-26.1.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:58c33dc0e185dd97a9ac0288b3188d1be12b756eda67490e6ed6a75cf9491d79"}, + {file = "pyzmq-26.1.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:68a0a1d83d33d8367ddddb3e6bb4afbb0f92bd1dac2c72cd5e5ddc86bdafd3eb"}, + {file = "pyzmq-26.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2ae7c57e22ad881af78075e0cea10a4c778e67234adc65c404391b417a4dda83"}, + {file = "pyzmq-26.1.0-cp312-cp312-win32.whl", hash = "sha256:347e84fc88cc4cb646597f6d3a7ea0998f887ee8dc31c08587e9c3fd7b5ccef3"}, + {file = "pyzmq-26.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:9f136a6e964830230912f75b5a116a21fe8e34128dcfd82285aa0ef07cb2c7bd"}, + {file = "pyzmq-26.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:a4b7a989c8f5a72ab1b2bbfa58105578753ae77b71ba33e7383a31ff75a504c4"}, + {file = "pyzmq-26.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d416f2088ac8f12daacffbc2e8918ef4d6be8568e9d7155c83b7cebed49d2322"}, + {file = "pyzmq-26.1.0-cp313-cp313-macosx_10_15_universal2.whl", hash = "sha256:ecb6c88d7946166d783a635efc89f9a1ff11c33d680a20df9657b6902a1d133b"}, + {file = "pyzmq-26.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:471312a7375571857a089342beccc1a63584315188560c7c0da7e0a23afd8a5c"}, + {file = "pyzmq-26.1.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e6cea102ffa16b737d11932c426f1dc14b5938cf7bc12e17269559c458ac334"}, + {file = "pyzmq-26.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec7248673ffc7104b54e4957cee38b2f3075a13442348c8d651777bf41aa45ee"}, + {file = "pyzmq-26.1.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:0614aed6f87d550b5cecb03d795f4ddbb1544b78d02a4bd5eecf644ec98a39f6"}, + {file = "pyzmq-26.1.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:e8746ce968be22a8a1801bf4a23e565f9687088580c3ed07af5846580dd97f76"}, + {file = "pyzmq-26.1.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:7688653574392d2eaeef75ddcd0b2de5b232d8730af29af56c5adf1df9ef8d6f"}, + {file = "pyzmq-26.1.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:8d4dac7d97f15c653a5fedcafa82626bd6cee1450ccdaf84ffed7ea14f2b07a4"}, + {file = "pyzmq-26.1.0-cp313-cp313-win32.whl", hash = "sha256:ccb42ca0a4a46232d716779421bbebbcad23c08d37c980f02cc3a6bd115ad277"}, + {file = "pyzmq-26.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:e1e5d0a25aea8b691a00d6b54b28ac514c8cc0d8646d05f7ca6cb64b97358250"}, + {file = "pyzmq-26.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:fc82269d24860cfa859b676d18850cbb8e312dcd7eada09e7d5b007e2f3d9eb1"}, + {file = "pyzmq-26.1.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:416ac51cabd54f587995c2b05421324700b22e98d3d0aa2cfaec985524d16f1d"}, + {file = "pyzmq-26.1.0-cp313-cp313t-macosx_10_15_universal2.whl", hash = "sha256:ff832cce719edd11266ca32bc74a626b814fff236824aa1aeaad399b69fe6eae"}, + {file = "pyzmq-26.1.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:393daac1bcf81b2a23e696b7b638eedc965e9e3d2112961a072b6cd8179ad2eb"}, + {file = "pyzmq-26.1.0-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9869fa984c8670c8ab899a719eb7b516860a29bc26300a84d24d8c1b71eae3ec"}, + {file = "pyzmq-26.1.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b3b8e36fd4c32c0825b4461372949ecd1585d326802b1321f8b6dc1d7e9318c"}, + {file = "pyzmq-26.1.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:3ee647d84b83509b7271457bb428cc347037f437ead4b0b6e43b5eba35fec0aa"}, + {file = "pyzmq-26.1.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:45cb1a70eb00405ce3893041099655265fabcd9c4e1e50c330026e82257892c1"}, + {file = "pyzmq-26.1.0-cp313-cp313t-musllinux_1_1_i686.whl", hash = "sha256:5cca7b4adb86d7470e0fc96037771981d740f0b4cb99776d5cb59cd0e6684a73"}, + {file = "pyzmq-26.1.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:91d1a20bdaf3b25f3173ff44e54b1cfbc05f94c9e8133314eb2962a89e05d6e3"}, + {file = "pyzmq-26.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c0665d85535192098420428c779361b8823d3d7ec4848c6af3abb93bc5c915bf"}, + {file = "pyzmq-26.1.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:96d7c1d35ee4a495df56c50c83df7af1c9688cce2e9e0edffdbf50889c167595"}, + {file = "pyzmq-26.1.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b281b5ff5fcc9dcbfe941ac5c7fcd4b6c065adad12d850f95c9d6f23c2652384"}, + {file = "pyzmq-26.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5384c527a9a004445c5074f1e20db83086c8ff1682a626676229aafd9cf9f7d1"}, + {file = "pyzmq-26.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:754c99a9840839375ee251b38ac5964c0f369306eddb56804a073b6efdc0cd88"}, + {file = "pyzmq-26.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:9bdfcb74b469b592972ed881bad57d22e2c0acc89f5e8c146782d0d90fb9f4bf"}, + {file = "pyzmq-26.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:bd13f0231f4788db619347b971ca5f319c5b7ebee151afc7c14632068c6261d3"}, + {file = "pyzmq-26.1.0-cp37-cp37m-win32.whl", hash = "sha256:c5668dac86a869349828db5fc928ee3f58d450dce2c85607067d581f745e4fb1"}, + {file = "pyzmq-26.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:ad875277844cfaeca7fe299ddf8c8d8bfe271c3dc1caf14d454faa5cdbf2fa7a"}, + {file = "pyzmq-26.1.0-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:65c6e03cc0222eaf6aad57ff4ecc0a070451e23232bb48db4322cc45602cede0"}, + {file = "pyzmq-26.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:038ae4ffb63e3991f386e7fda85a9baab7d6617fe85b74a8f9cab190d73adb2b"}, + {file = "pyzmq-26.1.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:bdeb2c61611293f64ac1073f4bf6723b67d291905308a7de9bb2ca87464e3273"}, + {file = "pyzmq-26.1.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:61dfa5ee9d7df297c859ac82b1226d8fefaf9c5113dc25c2c00ecad6feeeb04f"}, + {file = "pyzmq-26.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3292d384537b9918010769b82ab3e79fca8b23d74f56fc69a679106a3e2c2cf"}, + {file = "pyzmq-26.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f9499c70c19ff0fbe1007043acb5ad15c1dec7d8e84ab429bca8c87138e8f85c"}, + {file = "pyzmq-26.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:d3dd5523ed258ad58fed7e364c92a9360d1af8a9371e0822bd0146bdf017ef4c"}, + {file = "pyzmq-26.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:baba2fd199b098c5544ef2536b2499d2e2155392973ad32687024bd8572a7d1c"}, + {file = "pyzmq-26.1.0-cp38-cp38-win32.whl", hash = "sha256:ddbb2b386128d8eca92bd9ca74e80f73fe263bcca7aa419f5b4cbc1661e19741"}, + {file = "pyzmq-26.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:79e45a4096ec8388cdeb04a9fa5e9371583bcb826964d55b8b66cbffe7b33c86"}, + {file = "pyzmq-26.1.0-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:add52c78a12196bc0fda2de087ba6c876ea677cbda2e3eba63546b26e8bf177b"}, + {file = "pyzmq-26.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:98c03bd7f3339ff47de7ea9ac94a2b34580a8d4df69b50128bb6669e1191a895"}, + {file = "pyzmq-26.1.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:dcc37d9d708784726fafc9c5e1232de655a009dbf97946f117aefa38d5985a0f"}, + {file = "pyzmq-26.1.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5a6ed52f0b9bf8dcc64cc82cce0607a3dfed1dbb7e8c6f282adfccc7be9781de"}, + {file = "pyzmq-26.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:451e16ae8bea3d95649317b463c9f95cd9022641ec884e3d63fc67841ae86dfe"}, + {file = "pyzmq-26.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:906e532c814e1d579138177a00ae835cd6becbf104d45ed9093a3aaf658f6a6a"}, + {file = "pyzmq-26.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:05bacc4f94af468cc82808ae3293390278d5f3375bb20fef21e2034bb9a505b6"}, + {file = "pyzmq-26.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:57bb2acba798dc3740e913ffadd56b1fcef96f111e66f09e2a8db3050f1f12c8"}, + {file = "pyzmq-26.1.0-cp39-cp39-win32.whl", hash = "sha256:f774841bb0e8588505002962c02da420bcfb4c5056e87a139c6e45e745c0e2e2"}, + {file = "pyzmq-26.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:359c533bedc62c56415a1f5fcfd8279bc93453afdb0803307375ecf81c962402"}, + {file = "pyzmq-26.1.0-cp39-cp39-win_arm64.whl", hash = "sha256:7907419d150b19962138ecec81a17d4892ea440c184949dc29b358bc730caf69"}, + {file = "pyzmq-26.1.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:b24079a14c9596846bf7516fe75d1e2188d4a528364494859106a33d8b48be38"}, + {file = "pyzmq-26.1.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59d0acd2976e1064f1b398a00e2c3e77ed0a157529779e23087d4c2fb8aaa416"}, + {file = "pyzmq-26.1.0-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:911c43a4117915203c4cc8755e0f888e16c4676a82f61caee2f21b0c00e5b894"}, + {file = "pyzmq-26.1.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b10163e586cc609f5f85c9b233195554d77b1e9a0801388907441aaeb22841c5"}, + {file = "pyzmq-26.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:28a8b2abb76042f5fd7bd720f7fea48c0fd3e82e9de0a1bf2c0de3812ce44a42"}, + {file = "pyzmq-26.1.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:bef24d3e4ae2c985034439f449e3f9e06bf579974ce0e53d8a507a1577d5b2ab"}, + {file = "pyzmq-26.1.0-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2cd0f4d314f4a2518e8970b6f299ae18cff7c44d4a1fc06fc713f791c3a9e3ea"}, + {file = "pyzmq-26.1.0-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:fa25a620eed2a419acc2cf10135b995f8f0ce78ad00534d729aa761e4adcef8a"}, + {file = "pyzmq-26.1.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef3b048822dca6d231d8a8ba21069844ae38f5d83889b9b690bf17d2acc7d099"}, + {file = "pyzmq-26.1.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:9a6847c92d9851b59b9f33f968c68e9e441f9a0f8fc972c5580c5cd7cbc6ee24"}, + {file = "pyzmq-26.1.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:c9b9305004d7e4e6a824f4f19b6d8f32b3578aad6f19fc1122aaf320cbe3dc83"}, + {file = "pyzmq-26.1.0-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:63c1d3a65acb2f9c92dce03c4e1758cc552f1ae5c78d79a44e3bb88d2fa71f3a"}, + {file = "pyzmq-26.1.0-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d36b8fffe8b248a1b961c86fbdfa0129dfce878731d169ede7fa2631447331be"}, + {file = "pyzmq-26.1.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67976d12ebfd61a3bc7d77b71a9589b4d61d0422282596cf58c62c3866916544"}, + {file = "pyzmq-26.1.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:998444debc8816b5d8d15f966e42751032d0f4c55300c48cc337f2b3e4f17d03"}, + {file = "pyzmq-26.1.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e5c88b2f13bcf55fee78ea83567b9fe079ba1a4bef8b35c376043440040f7edb"}, + {file = "pyzmq-26.1.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d906d43e1592be4b25a587b7d96527cb67277542a5611e8ea9e996182fae410"}, + {file = "pyzmq-26.1.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:80b0c9942430d731c786545da6be96d824a41a51742e3e374fedd9018ea43106"}, + {file = "pyzmq-26.1.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:314d11564c00b77f6224d12eb3ddebe926c301e86b648a1835c5b28176c83eab"}, + {file = "pyzmq-26.1.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:093a1a3cae2496233f14b57f4b485da01b4ff764582c854c0f42c6dd2be37f3d"}, + {file = "pyzmq-26.1.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:3c397b1b450f749a7e974d74c06d69bd22dd362142f370ef2bd32a684d6b480c"}, + {file = "pyzmq-26.1.0.tar.gz", hash = "sha256:6c5aeea71f018ebd3b9115c7cb13863dd850e98ca6b9258509de1246461a7e7f"}, +] + +[package.dependencies] +cffi = {version = "*", markers = "implementation_name == \"pypy\""} + +[[package]] +name = "referencing" +version = "0.35.1" +description = "JSON Referencing + Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "referencing-0.35.1-py3-none-any.whl", hash = "sha256:eda6d3234d62814d1c64e305c1331c9a3a6132da475ab6382eaa997b21ee75de"}, + {file = "referencing-0.35.1.tar.gz", hash = "sha256:25b42124a6c8b632a425174f24087783efb348a6f1e0008e63cd4466fedf703c"}, +] + +[package.dependencies] +attrs = ">=22.2.0" +rpds-py = ">=0.7.0" + +[[package]] +name = "regex" +version = "2024.7.24" +description = "Alternative regular expression module, to replace re." +optional = false +python-versions = ">=3.8" +files = [ + {file = "regex-2024.7.24-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:228b0d3f567fafa0633aee87f08b9276c7062da9616931382993c03808bb68ce"}, + {file = "regex-2024.7.24-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3426de3b91d1bc73249042742f45c2148803c111d1175b283270177fdf669024"}, + {file = "regex-2024.7.24-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f273674b445bcb6e4409bf8d1be67bc4b58e8b46fd0d560055d515b8830063cd"}, + {file = "regex-2024.7.24-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23acc72f0f4e1a9e6e9843d6328177ae3074b4182167e34119ec7233dfeccf53"}, + {file = "regex-2024.7.24-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65fd3d2e228cae024c411c5ccdffae4c315271eee4a8b839291f84f796b34eca"}, + {file = "regex-2024.7.24-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c414cbda77dbf13c3bc88b073a1a9f375c7b0cb5e115e15d4b73ec3a2fbc6f59"}, + {file = "regex-2024.7.24-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf7a89eef64b5455835f5ed30254ec19bf41f7541cd94f266ab7cbd463f00c41"}, + {file = "regex-2024.7.24-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:19c65b00d42804e3fbea9708f0937d157e53429a39b7c61253ff15670ff62cb5"}, + {file = "regex-2024.7.24-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7a5486ca56c8869070a966321d5ab416ff0f83f30e0e2da1ab48815c8d165d46"}, + {file = "regex-2024.7.24-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6f51f9556785e5a203713f5efd9c085b4a45aecd2a42573e2b5041881b588d1f"}, + {file = "regex-2024.7.24-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:a4997716674d36a82eab3e86f8fa77080a5d8d96a389a61ea1d0e3a94a582cf7"}, + {file = "regex-2024.7.24-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:c0abb5e4e8ce71a61d9446040c1e86d4e6d23f9097275c5bd49ed978755ff0fe"}, + {file = "regex-2024.7.24-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:18300a1d78cf1290fa583cd8b7cde26ecb73e9f5916690cf9d42de569c89b1ce"}, + {file = "regex-2024.7.24-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:416c0e4f56308f34cdb18c3f59849479dde5b19febdcd6e6fa4d04b6c31c9faa"}, + {file = "regex-2024.7.24-cp310-cp310-win32.whl", hash = "sha256:fb168b5924bef397b5ba13aabd8cf5df7d3d93f10218d7b925e360d436863f66"}, + {file = "regex-2024.7.24-cp310-cp310-win_amd64.whl", hash = "sha256:6b9fc7e9cc983e75e2518496ba1afc524227c163e43d706688a6bb9eca41617e"}, + {file = "regex-2024.7.24-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:382281306e3adaaa7b8b9ebbb3ffb43358a7bbf585fa93821300a418bb975281"}, + {file = "regex-2024.7.24-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4fdd1384619f406ad9037fe6b6eaa3de2749e2e12084abc80169e8e075377d3b"}, + {file = "regex-2024.7.24-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3d974d24edb231446f708c455fd08f94c41c1ff4f04bcf06e5f36df5ef50b95a"}, + {file = "regex-2024.7.24-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a2ec4419a3fe6cf8a4795752596dfe0adb4aea40d3683a132bae9c30b81e8d73"}, + {file = "regex-2024.7.24-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eb563dd3aea54c797adf513eeec819c4213d7dbfc311874eb4fd28d10f2ff0f2"}, + {file = "regex-2024.7.24-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:45104baae8b9f67569f0f1dca5e1f1ed77a54ae1cd8b0b07aba89272710db61e"}, + {file = "regex-2024.7.24-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:994448ee01864501912abf2bad9203bffc34158e80fe8bfb5b031f4f8e16da51"}, + {file = "regex-2024.7.24-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3fac296f99283ac232d8125be932c5cd7644084a30748fda013028c815ba3364"}, + {file = "regex-2024.7.24-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7e37e809b9303ec3a179085415cb5f418ecf65ec98cdfe34f6a078b46ef823ee"}, + {file = "regex-2024.7.24-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:01b689e887f612610c869421241e075c02f2e3d1ae93a037cb14f88ab6a8934c"}, + {file = "regex-2024.7.24-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f6442f0f0ff81775eaa5b05af8a0ffa1dda36e9cf6ec1e0d3d245e8564b684ce"}, + {file = "regex-2024.7.24-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:871e3ab2838fbcb4e0865a6e01233975df3a15e6fce93b6f99d75cacbd9862d1"}, + {file = "regex-2024.7.24-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c918b7a1e26b4ab40409820ddccc5d49871a82329640f5005f73572d5eaa9b5e"}, + {file = "regex-2024.7.24-cp311-cp311-win32.whl", hash = "sha256:2dfbb8baf8ba2c2b9aa2807f44ed272f0913eeeba002478c4577b8d29cde215c"}, + {file = "regex-2024.7.24-cp311-cp311-win_amd64.whl", hash = "sha256:538d30cd96ed7d1416d3956f94d54e426a8daf7c14527f6e0d6d425fcb4cca52"}, + {file = "regex-2024.7.24-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:fe4ebef608553aff8deb845c7f4f1d0740ff76fa672c011cc0bacb2a00fbde86"}, + {file = "regex-2024.7.24-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:74007a5b25b7a678459f06559504f1eec2f0f17bca218c9d56f6a0a12bfffdad"}, + {file = "regex-2024.7.24-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7df9ea48641da022c2a3c9c641650cd09f0cd15e8908bf931ad538f5ca7919c9"}, + {file = "regex-2024.7.24-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a1141a1dcc32904c47f6846b040275c6e5de0bf73f17d7a409035d55b76f289"}, + {file = "regex-2024.7.24-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80c811cfcb5c331237d9bad3bea2c391114588cf4131707e84d9493064d267f9"}, + {file = "regex-2024.7.24-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7214477bf9bd195894cf24005b1e7b496f46833337b5dedb7b2a6e33f66d962c"}, + {file = "regex-2024.7.24-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d55588cba7553f0b6ec33130bc3e114b355570b45785cebdc9daed8c637dd440"}, + {file = "regex-2024.7.24-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:558a57cfc32adcf19d3f791f62b5ff564922942e389e3cfdb538a23d65a6b610"}, + {file = "regex-2024.7.24-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a512eed9dfd4117110b1881ba9a59b31433caed0c4101b361f768e7bcbaf93c5"}, + {file = "regex-2024.7.24-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:86b17ba823ea76256b1885652e3a141a99a5c4422f4a869189db328321b73799"}, + {file = "regex-2024.7.24-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5eefee9bfe23f6df09ffb6dfb23809f4d74a78acef004aa904dc7c88b9944b05"}, + {file = "regex-2024.7.24-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:731fcd76bbdbf225e2eb85b7c38da9633ad3073822f5ab32379381e8c3c12e94"}, + {file = "regex-2024.7.24-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:eaef80eac3b4cfbdd6de53c6e108b4c534c21ae055d1dbea2de6b3b8ff3def38"}, + {file = "regex-2024.7.24-cp312-cp312-win32.whl", hash = "sha256:185e029368d6f89f36e526764cf12bf8d6f0e3a2a7737da625a76f594bdfcbfc"}, + {file = "regex-2024.7.24-cp312-cp312-win_amd64.whl", hash = "sha256:2f1baff13cc2521bea83ab2528e7a80cbe0ebb2c6f0bfad15be7da3aed443908"}, + {file = "regex-2024.7.24-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:66b4c0731a5c81921e938dcf1a88e978264e26e6ac4ec96a4d21ae0354581ae0"}, + {file = "regex-2024.7.24-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:88ecc3afd7e776967fa16c80f974cb79399ee8dc6c96423321d6f7d4b881c92b"}, + {file = "regex-2024.7.24-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:64bd50cf16bcc54b274e20235bf8edbb64184a30e1e53873ff8d444e7ac656b2"}, + {file = "regex-2024.7.24-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eb462f0e346fcf41a901a126b50f8781e9a474d3927930f3490f38a6e73b6950"}, + {file = "regex-2024.7.24-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a82465ebbc9b1c5c50738536fdfa7cab639a261a99b469c9d4c7dcbb2b3f1e57"}, + {file = "regex-2024.7.24-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:68a8f8c046c6466ac61a36b65bb2395c74451df2ffb8458492ef49900efed293"}, + {file = "regex-2024.7.24-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac8e84fff5d27420f3c1e879ce9929108e873667ec87e0c8eeb413a5311adfe"}, + {file = "regex-2024.7.24-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba2537ef2163db9e6ccdbeb6f6424282ae4dea43177402152c67ef869cf3978b"}, + {file = "regex-2024.7.24-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:43affe33137fcd679bdae93fb25924979517e011f9dea99163f80b82eadc7e53"}, + {file = "regex-2024.7.24-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:c9bb87fdf2ab2370f21e4d5636e5317775e5d51ff32ebff2cf389f71b9b13750"}, + {file = "regex-2024.7.24-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:945352286a541406f99b2655c973852da7911b3f4264e010218bbc1cc73168f2"}, + {file = "regex-2024.7.24-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:8bc593dcce679206b60a538c302d03c29b18e3d862609317cb560e18b66d10cf"}, + {file = "regex-2024.7.24-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:3f3b6ca8eae6d6c75a6cff525c8530c60e909a71a15e1b731723233331de4169"}, + {file = "regex-2024.7.24-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:c51edc3541e11fbe83f0c4d9412ef6c79f664a3745fab261457e84465ec9d5a8"}, + {file = "regex-2024.7.24-cp38-cp38-win32.whl", hash = "sha256:d0a07763776188b4db4c9c7fb1b8c494049f84659bb387b71c73bbc07f189e96"}, + {file = "regex-2024.7.24-cp38-cp38-win_amd64.whl", hash = "sha256:8fd5afd101dcf86a270d254364e0e8dddedebe6bd1ab9d5f732f274fa00499a5"}, + {file = "regex-2024.7.24-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0ffe3f9d430cd37d8fa5632ff6fb36d5b24818c5c986893063b4e5bdb84cdf24"}, + {file = "regex-2024.7.24-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:25419b70ba00a16abc90ee5fce061228206173231f004437730b67ac77323f0d"}, + {file = "regex-2024.7.24-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:33e2614a7ce627f0cdf2ad104797d1f68342d967de3695678c0cb84f530709f8"}, + {file = "regex-2024.7.24-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d33a0021893ede5969876052796165bab6006559ab845fd7b515a30abdd990dc"}, + {file = "regex-2024.7.24-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:04ce29e2c5fedf296b1a1b0acc1724ba93a36fb14031f3abfb7abda2806c1535"}, + {file = "regex-2024.7.24-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b16582783f44fbca6fcf46f61347340c787d7530d88b4d590a397a47583f31dd"}, + {file = "regex-2024.7.24-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:836d3cc225b3e8a943d0b02633fb2f28a66e281290302a79df0e1eaa984ff7c1"}, + {file = "regex-2024.7.24-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:438d9f0f4bc64e8dea78274caa5af971ceff0f8771e1a2333620969936ba10be"}, + {file = "regex-2024.7.24-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:973335b1624859cb0e52f96062a28aa18f3a5fc77a96e4a3d6d76e29811a0e6e"}, + {file = "regex-2024.7.24-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:c5e69fd3eb0b409432b537fe3c6f44ac089c458ab6b78dcec14478422879ec5f"}, + {file = "regex-2024.7.24-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:fbf8c2f00904eaf63ff37718eb13acf8e178cb940520e47b2f05027f5bb34ce3"}, + {file = "regex-2024.7.24-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ae2757ace61bc4061b69af19e4689fa4416e1a04840f33b441034202b5cd02d4"}, + {file = "regex-2024.7.24-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:44fc61b99035fd9b3b9453f1713234e5a7c92a04f3577252b45feefe1b327759"}, + {file = "regex-2024.7.24-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:84c312cdf839e8b579f504afcd7b65f35d60b6285d892b19adea16355e8343c9"}, + {file = "regex-2024.7.24-cp39-cp39-win32.whl", hash = "sha256:ca5b2028c2f7af4e13fb9fc29b28d0ce767c38c7facdf64f6c2cd040413055f1"}, + {file = "regex-2024.7.24-cp39-cp39-win_amd64.whl", hash = "sha256:7c479f5ae937ec9985ecaf42e2e10631551d909f203e31308c12d703922742f9"}, + {file = "regex-2024.7.24.tar.gz", hash = "sha256:9cfd009eed1a46b27c14039ad5bbc5e71b6367c5b2e6d5f5da0ea91600817506"}, +] + +[[package]] +name = "requests" +version = "2.32.3" +description = "Python HTTP for Humans." +optional = false +python-versions = ">=3.8" +files = [ + {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, + {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "rich" +version = "13.7.1" +description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "rich-13.7.1-py3-none-any.whl", hash = "sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222"}, + {file = "rich-13.7.1.tar.gz", hash = "sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432"}, +] + +[package.dependencies] +markdown-it-py = ">=2.2.0" +pygments = ">=2.13.0,<3.0.0" + +[package.extras] +jupyter = ["ipywidgets (>=7.5.1,<9)"] + +[[package]] +name = "rpds-py" +version = "0.20.0" +description = "Python bindings to Rust's persistent data structures (rpds)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "rpds_py-0.20.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3ad0fda1635f8439cde85c700f964b23ed5fc2d28016b32b9ee5fe30da5c84e2"}, + {file = "rpds_py-0.20.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9bb4a0d90fdb03437c109a17eade42dfbf6190408f29b2744114d11586611d6f"}, + {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6377e647bbfd0a0b159fe557f2c6c602c159fc752fa316572f012fc0bf67150"}, + {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb851b7df9dda52dc1415ebee12362047ce771fc36914586b2e9fcbd7d293b3e"}, + {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1e0f80b739e5a8f54837be5d5c924483996b603d5502bfff79bf33da06164ee2"}, + {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5a8c94dad2e45324fc74dce25e1645d4d14df9a4e54a30fa0ae8bad9a63928e3"}, + {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8e604fe73ba048c06085beaf51147eaec7df856824bfe7b98657cf436623daf"}, + {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:df3de6b7726b52966edf29663e57306b23ef775faf0ac01a3e9f4012a24a4140"}, + {file = "rpds_py-0.20.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf258ede5bc22a45c8e726b29835b9303c285ab46fc7c3a4cc770736b5304c9f"}, + {file = "rpds_py-0.20.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:55fea87029cded5df854ca7e192ec7bdb7ecd1d9a3f63d5c4eb09148acf4a7ce"}, + {file = "rpds_py-0.20.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ae94bd0b2f02c28e199e9bc51485d0c5601f58780636185660f86bf80c89af94"}, + {file = "rpds_py-0.20.0-cp310-none-win32.whl", hash = "sha256:28527c685f237c05445efec62426d285e47a58fb05ba0090a4340b73ecda6dee"}, + {file = "rpds_py-0.20.0-cp310-none-win_amd64.whl", hash = "sha256:238a2d5b1cad28cdc6ed15faf93a998336eb041c4e440dd7f902528b8891b399"}, + {file = "rpds_py-0.20.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:ac2f4f7a98934c2ed6505aead07b979e6f999389f16b714448fb39bbaa86a489"}, + {file = "rpds_py-0.20.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:220002c1b846db9afd83371d08d239fdc865e8f8c5795bbaec20916a76db3318"}, + {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d7919548df3f25374a1f5d01fbcd38dacab338ef5f33e044744b5c36729c8db"}, + {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:758406267907b3781beee0f0edfe4a179fbd97c0be2e9b1154d7f0a1279cf8e5"}, + {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3d61339e9f84a3f0767b1995adfb171a0d00a1185192718a17af6e124728e0f5"}, + {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1259c7b3705ac0a0bd38197565a5d603218591d3f6cee6e614e380b6ba61c6f6"}, + {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c1dc0f53856b9cc9a0ccca0a7cc61d3d20a7088201c0937f3f4048c1718a209"}, + {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7e60cb630f674a31f0368ed32b2a6b4331b8350d67de53c0359992444b116dd3"}, + {file = "rpds_py-0.20.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:dbe982f38565bb50cb7fb061ebf762c2f254ca3d8c20d4006878766e84266272"}, + {file = "rpds_py-0.20.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:514b3293b64187172bc77c8fb0cdae26981618021053b30d8371c3a902d4d5ad"}, + {file = "rpds_py-0.20.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d0a26ffe9d4dd35e4dfdd1e71f46401cff0181c75ac174711ccff0459135fa58"}, + {file = "rpds_py-0.20.0-cp311-none-win32.whl", hash = "sha256:89c19a494bf3ad08c1da49445cc5d13d8fefc265f48ee7e7556839acdacf69d0"}, + {file = "rpds_py-0.20.0-cp311-none-win_amd64.whl", hash = "sha256:c638144ce971df84650d3ed0096e2ae7af8e62ecbbb7b201c8935c370df00a2c"}, + {file = "rpds_py-0.20.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a84ab91cbe7aab97f7446652d0ed37d35b68a465aeef8fc41932a9d7eee2c1a6"}, + {file = "rpds_py-0.20.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:56e27147a5a4c2c21633ff8475d185734c0e4befd1c989b5b95a5d0db699b21b"}, + {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2580b0c34583b85efec8c5c5ec9edf2dfe817330cc882ee972ae650e7b5ef739"}, + {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b80d4a7900cf6b66bb9cee5c352b2d708e29e5a37fe9bf784fa97fc11504bf6c"}, + {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:50eccbf054e62a7b2209b28dc7a22d6254860209d6753e6b78cfaeb0075d7bee"}, + {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:49a8063ea4296b3a7e81a5dfb8f7b2d73f0b1c20c2af401fb0cdf22e14711a96"}, + {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea438162a9fcbee3ecf36c23e6c68237479f89f962f82dae83dc15feeceb37e4"}, + {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:18d7585c463087bddcfa74c2ba267339f14f2515158ac4db30b1f9cbdb62c8ef"}, + {file = "rpds_py-0.20.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d4c7d1a051eeb39f5c9547e82ea27cbcc28338482242e3e0b7768033cb083821"}, + {file = "rpds_py-0.20.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e4df1e3b3bec320790f699890d41c59d250f6beda159ea3c44c3f5bac1976940"}, + {file = "rpds_py-0.20.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2cf126d33a91ee6eedc7f3197b53e87a2acdac63602c0f03a02dd69e4b138174"}, + {file = "rpds_py-0.20.0-cp312-none-win32.whl", hash = "sha256:8bc7690f7caee50b04a79bf017a8d020c1f48c2a1077ffe172abec59870f1139"}, + {file = "rpds_py-0.20.0-cp312-none-win_amd64.whl", hash = "sha256:0e13e6952ef264c40587d510ad676a988df19adea20444c2b295e536457bc585"}, + {file = "rpds_py-0.20.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:aa9a0521aeca7d4941499a73ad7d4f8ffa3d1affc50b9ea11d992cd7eff18a29"}, + {file = "rpds_py-0.20.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4a1f1d51eccb7e6c32ae89243cb352389228ea62f89cd80823ea7dd1b98e0b91"}, + {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a86a9b96070674fc88b6f9f71a97d2c1d3e5165574615d1f9168ecba4cecb24"}, + {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6c8ef2ebf76df43f5750b46851ed1cdf8f109d7787ca40035fe19fbdc1acc5a7"}, + {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b74b25f024b421d5859d156750ea9a65651793d51b76a2e9238c05c9d5f203a9"}, + {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57eb94a8c16ab08fef6404301c38318e2c5a32216bf5de453e2714c964c125c8"}, + {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1940dae14e715e2e02dfd5b0f64a52e8374a517a1e531ad9412319dc3ac7879"}, + {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d20277fd62e1b992a50c43f13fbe13277a31f8c9f70d59759c88f644d66c619f"}, + {file = "rpds_py-0.20.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:06db23d43f26478303e954c34c75182356ca9aa7797d22c5345b16871ab9c45c"}, + {file = "rpds_py-0.20.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b2a5db5397d82fa847e4c624b0c98fe59d2d9b7cf0ce6de09e4d2e80f8f5b3f2"}, + {file = "rpds_py-0.20.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5a35df9f5548fd79cb2f52d27182108c3e6641a4feb0f39067911bf2adaa3e57"}, + {file = "rpds_py-0.20.0-cp313-none-win32.whl", hash = "sha256:fd2d84f40633bc475ef2d5490b9c19543fbf18596dcb1b291e3a12ea5d722f7a"}, + {file = "rpds_py-0.20.0-cp313-none-win_amd64.whl", hash = "sha256:9bc2d153989e3216b0559251b0c260cfd168ec78b1fac33dd485750a228db5a2"}, + {file = "rpds_py-0.20.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:f2fbf7db2012d4876fb0d66b5b9ba6591197b0f165db8d99371d976546472a24"}, + {file = "rpds_py-0.20.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1e5f3cd7397c8f86c8cc72d5a791071431c108edd79872cdd96e00abd8497d29"}, + {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce9845054c13696f7af7f2b353e6b4f676dab1b4b215d7fe5e05c6f8bb06f965"}, + {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c3e130fd0ec56cb76eb49ef52faead8ff09d13f4527e9b0c400307ff72b408e1"}, + {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4b16aa0107ecb512b568244ef461f27697164d9a68d8b35090e9b0c1c8b27752"}, + {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aa7f429242aae2947246587d2964fad750b79e8c233a2367f71b554e9447949c"}, + {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af0fc424a5842a11e28956e69395fbbeab2c97c42253169d87e90aac2886d751"}, + {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b8c00a3b1e70c1d3891f0db1b05292747f0dbcfb49c43f9244d04c70fbc40eb8"}, + {file = "rpds_py-0.20.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:40ce74fc86ee4645d0a225498d091d8bc61f39b709ebef8204cb8b5a464d3c0e"}, + {file = "rpds_py-0.20.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:4fe84294c7019456e56d93e8ababdad5a329cd25975be749c3f5f558abb48253"}, + {file = "rpds_py-0.20.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:338ca4539aad4ce70a656e5187a3a31c5204f261aef9f6ab50e50bcdffaf050a"}, + {file = "rpds_py-0.20.0-cp38-none-win32.whl", hash = "sha256:54b43a2b07db18314669092bb2de584524d1ef414588780261e31e85846c26a5"}, + {file = "rpds_py-0.20.0-cp38-none-win_amd64.whl", hash = "sha256:a1862d2d7ce1674cffa6d186d53ca95c6e17ed2b06b3f4c476173565c862d232"}, + {file = "rpds_py-0.20.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:3fde368e9140312b6e8b6c09fb9f8c8c2f00999d1823403ae90cc00480221b22"}, + {file = "rpds_py-0.20.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9824fb430c9cf9af743cf7aaf6707bf14323fb51ee74425c380f4c846ea70789"}, + {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:11ef6ce74616342888b69878d45e9f779b95d4bd48b382a229fe624a409b72c5"}, + {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c52d3f2f82b763a24ef52f5d24358553e8403ce05f893b5347098014f2d9eff2"}, + {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d35cef91e59ebbeaa45214861874bc6f19eb35de96db73e467a8358d701a96c"}, + {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d72278a30111e5b5525c1dd96120d9e958464316f55adb030433ea905866f4de"}, + {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4c29cbbba378759ac5786730d1c3cb4ec6f8ababf5c42a9ce303dc4b3d08cda"}, + {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6632f2d04f15d1bd6fe0eedd3b86d9061b836ddca4c03d5cf5c7e9e6b7c14580"}, + {file = "rpds_py-0.20.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:d0b67d87bb45ed1cd020e8fbf2307d449b68abc45402fe1a4ac9e46c3c8b192b"}, + {file = "rpds_py-0.20.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:ec31a99ca63bf3cd7f1a5ac9fe95c5e2d060d3c768a09bc1d16e235840861420"}, + {file = "rpds_py-0.20.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:22e6c9976e38f4d8c4a63bd8a8edac5307dffd3ee7e6026d97f3cc3a2dc02a0b"}, + {file = "rpds_py-0.20.0-cp39-none-win32.whl", hash = "sha256:569b3ea770c2717b730b61998b6c54996adee3cef69fc28d444f3e7920313cf7"}, + {file = "rpds_py-0.20.0-cp39-none-win_amd64.whl", hash = "sha256:e6900ecdd50ce0facf703f7a00df12374b74bbc8ad9fe0f6559947fb20f82364"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:617c7357272c67696fd052811e352ac54ed1d9b49ab370261a80d3b6ce385045"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9426133526f69fcaba6e42146b4e12d6bc6c839b8b555097020e2b78ce908dcc"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:deb62214c42a261cb3eb04d474f7155279c1a8a8c30ac89b7dcb1721d92c3c02"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fcaeb7b57f1a1e071ebd748984359fef83ecb026325b9d4ca847c95bc7311c92"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d454b8749b4bd70dd0a79f428731ee263fa6995f83ccb8bada706e8d1d3ff89d"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d807dc2051abe041b6649681dce568f8e10668e3c1c6543ebae58f2d7e617855"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3c20f0ddeb6e29126d45f89206b8291352b8c5b44384e78a6499d68b52ae511"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b7f19250ceef892adf27f0399b9e5afad019288e9be756d6919cb58892129f51"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:4f1ed4749a08379555cebf4650453f14452eaa9c43d0a95c49db50c18b7da075"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:dcedf0b42bcb4cfff4101d7771a10532415a6106062f005ab97d1d0ab5681c60"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:39ed0d010457a78f54090fafb5d108501b5aa5604cc22408fc1c0c77eac14344"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:bb273176be34a746bdac0b0d7e4e2c467323d13640b736c4c477881a3220a989"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f918a1a130a6dfe1d7fe0f105064141342e7dd1611f2e6a21cd2f5c8cb1cfb3e"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:f60012a73aa396be721558caa3a6fd49b3dd0033d1675c6d59c4502e870fcf0c"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d2b1ad682a3dfda2a4e8ad8572f3100f95fad98cb99faf37ff0ddfe9cbf9d03"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:614fdafe9f5f19c63ea02817fa4861c606a59a604a77c8cdef5aa01d28b97921"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fa518bcd7600c584bf42e6617ee8132869e877db2f76bcdc281ec6a4113a53ab"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0475242f447cc6cb8a9dd486d68b2ef7fbee84427124c232bff5f63b1fe11e5"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f90a4cd061914a60bd51c68bcb4357086991bd0bb93d8aa66a6da7701370708f"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:def7400461c3a3f26e49078302e1c1b38f6752342c77e3cf72ce91ca69fb1bc1"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:65794e4048ee837494aea3c21a28ad5fc080994dfba5b036cf84de37f7ad5074"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:faefcc78f53a88f3076b7f8be0a8f8d35133a3ecf7f3770895c25f8813460f08"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:5b4f105deeffa28bbcdff6c49b34e74903139afa690e35d2d9e3c2c2fba18cec"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:fdfc3a892927458d98f3d55428ae46b921d1f7543b89382fdb483f5640daaec8"}, + {file = "rpds_py-0.20.0.tar.gz", hash = "sha256:d72a210824facfdaf8768cf2d7ca25a042c30320b3020de2fa04640920d4e121"}, +] + +[[package]] +name = "ruff" +version = "0.5.7" +description = "An extremely fast Python linter and code formatter, written in Rust." +optional = false +python-versions = ">=3.7" +files = [ + {file = "ruff-0.5.7-py3-none-linux_armv6l.whl", hash = "sha256:548992d342fc404ee2e15a242cdbea4f8e39a52f2e7752d0e4cbe88d2d2f416a"}, + {file = "ruff-0.5.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:00cc8872331055ee017c4f1071a8a31ca0809ccc0657da1d154a1d2abac5c0be"}, + {file = "ruff-0.5.7-py3-none-macosx_11_0_arm64.whl", hash = "sha256:eaf3d86a1fdac1aec8a3417a63587d93f906c678bb9ed0b796da7b59c1114a1e"}, + {file = "ruff-0.5.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a01c34400097b06cf8a6e61b35d6d456d5bd1ae6961542de18ec81eaf33b4cb8"}, + {file = "ruff-0.5.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fcc8054f1a717e2213500edaddcf1dbb0abad40d98e1bd9d0ad364f75c763eea"}, + {file = "ruff-0.5.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7f70284e73f36558ef51602254451e50dd6cc479f8b6f8413a95fcb5db4a55fc"}, + {file = "ruff-0.5.7-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:a78ad870ae3c460394fc95437d43deb5c04b5c29297815a2a1de028903f19692"}, + {file = "ruff-0.5.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9ccd078c66a8e419475174bfe60a69adb36ce04f8d4e91b006f1329d5cd44bcf"}, + {file = "ruff-0.5.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7e31c9bad4ebf8fdb77b59cae75814440731060a09a0e0077d559a556453acbb"}, + {file = "ruff-0.5.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d796327eed8e168164346b769dd9a27a70e0298d667b4ecee6877ce8095ec8e"}, + {file = "ruff-0.5.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:4a09ea2c3f7778cc635e7f6edf57d566a8ee8f485f3c4454db7771efb692c499"}, + {file = "ruff-0.5.7-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:a36d8dcf55b3a3bc353270d544fb170d75d2dff41eba5df57b4e0b67a95bb64e"}, + {file = "ruff-0.5.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:9369c218f789eefbd1b8d82a8cf25017b523ac47d96b2f531eba73770971c9e5"}, + {file = "ruff-0.5.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:b88ca3db7eb377eb24fb7c82840546fb7acef75af4a74bd36e9ceb37a890257e"}, + {file = "ruff-0.5.7-py3-none-win32.whl", hash = "sha256:33d61fc0e902198a3e55719f4be6b375b28f860b09c281e4bdbf783c0566576a"}, + {file = "ruff-0.5.7-py3-none-win_amd64.whl", hash = "sha256:083bbcbe6fadb93cd86709037acc510f86eed5a314203079df174c40bbbca6b3"}, + {file = "ruff-0.5.7-py3-none-win_arm64.whl", hash = "sha256:2dca26154ff9571995107221d0aeaad0e75a77b5a682d6236cf89a58c70b76f4"}, + {file = "ruff-0.5.7.tar.gz", hash = "sha256:8dfc0a458797f5d9fb622dd0efc52d796f23f0a1493a9527f4e49a550ae9a7e5"}, +] + +[[package]] +name = "safetensors" +version = "0.4.4" +description = "" +optional = false +python-versions = ">=3.7" +files = [ + {file = "safetensors-0.4.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2adb497ada13097f30e386e88c959c0fda855a5f6f98845710f5bb2c57e14f12"}, + {file = "safetensors-0.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7db7fdc2d71fd1444d85ca3f3d682ba2df7d61a637dfc6d80793f439eae264ab"}, + {file = "safetensors-0.4.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d4f0eed76b430f009fbefca1a0028ddb112891b03cb556d7440d5cd68eb89a9"}, + {file = "safetensors-0.4.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:57d216fab0b5c432aabf7170883d7c11671622bde8bd1436c46d633163a703f6"}, + {file = "safetensors-0.4.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7d9b76322e49c056bcc819f8bdca37a2daa5a6d42c07f30927b501088db03309"}, + {file = "safetensors-0.4.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:32f0d1f6243e90ee43bc6ee3e8c30ac5b09ca63f5dd35dbc985a1fc5208c451a"}, + {file = "safetensors-0.4.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:44d464bdc384874601a177375028012a5f177f1505279f9456fea84bbc575c7f"}, + {file = "safetensors-0.4.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:63144e36209ad8e4e65384dbf2d52dd5b1866986079c00a72335402a38aacdc5"}, + {file = "safetensors-0.4.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:051d5ecd490af7245258000304b812825974d5e56f14a3ff7e1b8b2ba6dc2ed4"}, + {file = "safetensors-0.4.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:51bc8429d9376224cd3cf7e8ce4f208b4c930cd10e515b6ac6a72cbc3370f0d9"}, + {file = "safetensors-0.4.4-cp310-none-win32.whl", hash = "sha256:fb7b54830cee8cf9923d969e2df87ce20e625b1af2fd194222ab902d3adcc29c"}, + {file = "safetensors-0.4.4-cp310-none-win_amd64.whl", hash = "sha256:4b3e8aa8226d6560de8c2b9d5ff8555ea482599c670610758afdc97f3e021e9c"}, + {file = "safetensors-0.4.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:bbaa31f2cb49013818bde319232ccd72da62ee40f7d2aa532083eda5664e85ff"}, + {file = "safetensors-0.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9fdcb80f4e9fbb33b58e9bf95e7dbbedff505d1bcd1c05f7c7ce883632710006"}, + {file = "safetensors-0.4.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55c14c20be247b8a1aeaf3ab4476265e3ca83096bb8e09bb1a7aa806088def4f"}, + {file = "safetensors-0.4.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:949aaa1118660f992dbf0968487b3e3cfdad67f948658ab08c6b5762e90cc8b6"}, + {file = "safetensors-0.4.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c11a4ab7debc456326a2bac67f35ee0ac792bcf812c7562a4a28559a5c795e27"}, + {file = "safetensors-0.4.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c0cea44bba5c5601b297bc8307e4075535b95163402e4906b2e9b82788a2a6df"}, + {file = "safetensors-0.4.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9d752c97f6bbe327352f76e5b86442d776abc789249fc5e72eacb49e6916482"}, + {file = "safetensors-0.4.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:03f2bb92e61b055ef6cc22883ad1ae898010a95730fa988c60a23800eb742c2c"}, + {file = "safetensors-0.4.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:87bf3f91a9328a941acc44eceffd4e1f5f89b030985b2966637e582157173b98"}, + {file = "safetensors-0.4.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:20d218ec2b6899d29d6895419a58b6e44cc5ff8f0cc29fac8d236a8978ab702e"}, + {file = "safetensors-0.4.4-cp311-none-win32.whl", hash = "sha256:8079486118919f600c603536e2490ca37b3dbd3280e3ad6eaacfe6264605ac8a"}, + {file = "safetensors-0.4.4-cp311-none-win_amd64.whl", hash = "sha256:2f8c2eb0615e2e64ee27d478c7c13f51e5329d7972d9e15528d3e4cfc4a08f0d"}, + {file = "safetensors-0.4.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:baec5675944b4a47749c93c01c73d826ef7d42d36ba8d0dba36336fa80c76426"}, + {file = "safetensors-0.4.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f15117b96866401825f3e94543145028a2947d19974429246ce59403f49e77c6"}, + {file = "safetensors-0.4.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a13a9caea485df164c51be4eb0c87f97f790b7c3213d635eba2314d959fe929"}, + {file = "safetensors-0.4.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6b54bc4ca5f9b9bba8cd4fb91c24b2446a86b5ae7f8975cf3b7a277353c3127c"}, + {file = "safetensors-0.4.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:08332c22e03b651c8eb7bf5fc2de90044f3672f43403b3d9ac7e7e0f4f76495e"}, + {file = "safetensors-0.4.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bb62841e839ee992c37bb75e75891c7f4904e772db3691c59daaca5b4ab960e1"}, + {file = "safetensors-0.4.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e5b927acc5f2f59547270b0309a46d983edc44be64e1ca27a7fcb0474d6cd67"}, + {file = "safetensors-0.4.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2a69c71b1ae98a8021a09a0b43363b0143b0ce74e7c0e83cacba691b62655fb8"}, + {file = "safetensors-0.4.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:23654ad162c02a5636f0cd520a0310902c4421aab1d91a0b667722a4937cc445"}, + {file = "safetensors-0.4.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:0677c109d949cf53756859160b955b2e75b0eefe952189c184d7be30ecf7e858"}, + {file = "safetensors-0.4.4-cp312-none-win32.whl", hash = "sha256:a51d0ddd4deb8871c6de15a772ef40b3dbd26a3c0451bb9e66bc76fc5a784e5b"}, + {file = "safetensors-0.4.4-cp312-none-win_amd64.whl", hash = "sha256:2d065059e75a798bc1933c293b68d04d79b586bb7f8c921e0ca1e82759d0dbb1"}, + {file = "safetensors-0.4.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:9d625692578dd40a112df30c02a1adf068027566abd8e6a74893bb13d441c150"}, + {file = "safetensors-0.4.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7cabcf39c81e5b988d0adefdaea2eb9b4fd9bd62d5ed6559988c62f36bfa9a89"}, + {file = "safetensors-0.4.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8359bef65f49d51476e9811d59c015f0ddae618ee0e44144f5595278c9f8268c"}, + {file = "safetensors-0.4.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1a32c662e7df9226fd850f054a3ead0e4213a96a70b5ce37b2d26ba27004e013"}, + {file = "safetensors-0.4.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c329a4dcc395364a1c0d2d1574d725fe81a840783dda64c31c5a60fc7d41472c"}, + {file = "safetensors-0.4.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:239ee093b1db877c9f8fe2d71331a97f3b9c7c0d3ab9f09c4851004a11f44b65"}, + {file = "safetensors-0.4.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd574145d930cf9405a64f9923600879a5ce51d9f315443a5f706374841327b6"}, + {file = "safetensors-0.4.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f6784eed29f9e036acb0b7769d9e78a0dc2c72c2d8ba7903005350d817e287a4"}, + {file = "safetensors-0.4.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:65a4a6072436bf0a4825b1c295d248cc17e5f4651e60ee62427a5bcaa8622a7a"}, + {file = "safetensors-0.4.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:df81e3407630de060ae8313da49509c3caa33b1a9415562284eaf3d0c7705f9f"}, + {file = "safetensors-0.4.4-cp37-cp37m-macosx_10_12_x86_64.whl", hash = "sha256:e4a0f374200e8443d9746e947ebb346c40f83a3970e75a685ade0adbba5c48d9"}, + {file = "safetensors-0.4.4-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:181fb5f3dee78dae7fd7ec57d02e58f7936498d587c6b7c1c8049ef448c8d285"}, + {file = "safetensors-0.4.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb4ac1d8f6b65ec84ddfacd275079e89d9df7c92f95675ba96c4f790a64df6e"}, + {file = "safetensors-0.4.4-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:76897944cd9239e8a70955679b531b9a0619f76e25476e57ed373322d9c2075d"}, + {file = "safetensors-0.4.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2a9e9d1a27e51a0f69e761a3d581c3af46729ec1c988fa1f839e04743026ae35"}, + {file = "safetensors-0.4.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:005ef9fc0f47cb9821c40793eb029f712e97278dae84de91cb2b4809b856685d"}, + {file = "safetensors-0.4.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26987dac3752688c696c77c3576f951dbbdb8c57f0957a41fb6f933cf84c0b62"}, + {file = "safetensors-0.4.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c05270b290acd8d249739f40d272a64dd597d5a4b90f27d830e538bc2549303c"}, + {file = "safetensors-0.4.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:068d3a33711fc4d93659c825a04480ff5a3854e1d78632cdc8f37fee917e8a60"}, + {file = "safetensors-0.4.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:063421ef08ca1021feea8b46951251b90ae91f899234dd78297cbe7c1db73b99"}, + {file = "safetensors-0.4.4-cp37-none-win32.whl", hash = "sha256:d52f5d0615ea83fd853d4e1d8acf93cc2e0223ad4568ba1e1f6ca72e94ea7b9d"}, + {file = "safetensors-0.4.4-cp37-none-win_amd64.whl", hash = "sha256:88a5ac3280232d4ed8e994cbc03b46a1807ce0aa123867b40c4a41f226c61f94"}, + {file = "safetensors-0.4.4-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:3467ab511bfe3360967d7dc53b49f272d59309e57a067dd2405b4d35e7dcf9dc"}, + {file = "safetensors-0.4.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2ab4c96d922e53670ce25fbb9b63d5ea972e244de4fa1dd97b590d9fd66aacef"}, + {file = "safetensors-0.4.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87df18fce4440477c3ef1fd7ae17c704a69a74a77e705a12be135ee0651a0c2d"}, + {file = "safetensors-0.4.4-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0e5fe345b2bc7d88587149ac11def1f629d2671c4c34f5df38aed0ba59dc37f8"}, + {file = "safetensors-0.4.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9f1a3e01dce3cd54060791e7e24588417c98b941baa5974700eeb0b8eb65b0a0"}, + {file = "safetensors-0.4.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c6bf35e9a8998d8339fd9a05ac4ce465a4d2a2956cc0d837b67c4642ed9e947"}, + {file = "safetensors-0.4.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:166c0c52f6488b8538b2a9f3fbc6aad61a7261e170698779b371e81b45f0440d"}, + {file = "safetensors-0.4.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:87e9903b8668a16ef02c08ba4ebc91e57a49c481e9b5866e31d798632805014b"}, + {file = "safetensors-0.4.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a9c421153aa23c323bd8483d4155b4eee82c9a50ac11cccd83539104a8279c64"}, + {file = "safetensors-0.4.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a4b8617499b2371c7353302c5116a7e0a3a12da66389ce53140e607d3bf7b3d3"}, + {file = "safetensors-0.4.4-cp38-none-win32.whl", hash = "sha256:c6280f5aeafa1731f0a3709463ab33d8e0624321593951aefada5472f0b313fd"}, + {file = "safetensors-0.4.4-cp38-none-win_amd64.whl", hash = "sha256:6ceed6247fc2d33b2a7b7d25d8a0fe645b68798856e0bc7a9800c5fd945eb80f"}, + {file = "safetensors-0.4.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:5cf6c6f6193797372adf50c91d0171743d16299491c75acad8650107dffa9269"}, + {file = "safetensors-0.4.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:419010156b914a3e5da4e4adf992bee050924d0fe423c4b329e523e2c14c3547"}, + {file = "safetensors-0.4.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88f6fd5a5c1302ce79993cc5feeadcc795a70f953c762544d01fb02b2db4ea33"}, + {file = "safetensors-0.4.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d468cffb82d90789696d5b4d8b6ab8843052cba58a15296691a7a3df55143cd2"}, + {file = "safetensors-0.4.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9353c2af2dd467333d4850a16edb66855e795561cd170685178f706c80d2c71e"}, + {file = "safetensors-0.4.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:83c155b4a33368d9b9c2543e78f2452090fb030c52401ca608ef16fa58c98353"}, + {file = "safetensors-0.4.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9850754c434e636ce3dc586f534bb23bcbd78940c304775bee9005bf610e98f1"}, + {file = "safetensors-0.4.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:275f500b4d26f67b6ec05629a4600645231bd75e4ed42087a7c1801bff04f4b3"}, + {file = "safetensors-0.4.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5c2308de665b7130cd0e40a2329278226e4cf083f7400c51ca7e19ccfb3886f3"}, + {file = "safetensors-0.4.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e06a9ebc8656e030ccfe44634f2a541b4b1801cd52e390a53ad8bacbd65f8518"}, + {file = "safetensors-0.4.4-cp39-none-win32.whl", hash = "sha256:ef73df487b7c14b477016947c92708c2d929e1dee2bacdd6fff5a82ed4539537"}, + {file = "safetensors-0.4.4-cp39-none-win_amd64.whl", hash = "sha256:83d054818a8d1198d8bd8bc3ea2aac112a2c19def2bf73758321976788706398"}, + {file = "safetensors-0.4.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:1d1f34c71371f0e034004a0b583284b45d233dd0b5f64a9125e16b8a01d15067"}, + {file = "safetensors-0.4.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1a8043a33d58bc9b30dfac90f75712134ca34733ec3d8267b1bd682afe7194f5"}, + {file = "safetensors-0.4.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8db8f0c59c84792c12661f8efa85de160f80efe16b87a9d5de91b93f9e0bce3c"}, + {file = "safetensors-0.4.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cfc1fc38e37630dd12d519bdec9dcd4b345aec9930bb9ce0ed04461f49e58b52"}, + {file = "safetensors-0.4.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e5c9d86d9b13b18aafa88303e2cd21e677f5da2a14c828d2c460fe513af2e9a5"}, + {file = "safetensors-0.4.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:43251d7f29a59120a26f5a0d9583b9e112999e500afabcfdcb91606d3c5c89e3"}, + {file = "safetensors-0.4.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:2c42e9b277513b81cf507e6121c7b432b3235f980cac04f39f435b7902857f91"}, + {file = "safetensors-0.4.4-pp37-pypy37_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3daacc9a4e3f428a84dd56bf31f20b768eb0b204af891ed68e1f06db9edf546f"}, + {file = "safetensors-0.4.4-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:218bbb9b883596715fc9997bb42470bf9f21bb832c3b34c2bf744d6fa8f2bbba"}, + {file = "safetensors-0.4.4-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7bd5efc26b39f7fc82d4ab1d86a7f0644c8e34f3699c33f85bfa9a717a030e1b"}, + {file = "safetensors-0.4.4-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:56ad9776b65d8743f86698a1973292c966cf3abff627efc44ed60e66cc538ddd"}, + {file = "safetensors-0.4.4-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:30f23e6253c5f43a809dea02dc28a9f5fa747735dc819f10c073fe1b605e97d4"}, + {file = "safetensors-0.4.4-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:5512078d00263de6cb04e9d26c9ae17611098f52357fea856213e38dc462f81f"}, + {file = "safetensors-0.4.4-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b96c3d9266439d17f35fc2173111d93afc1162f168e95aed122c1ca517b1f8f1"}, + {file = "safetensors-0.4.4-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:08d464aa72a9a13826946b4fb9094bb4b16554bbea2e069e20bd903289b6ced9"}, + {file = "safetensors-0.4.4-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:210160816d5a36cf41f48f38473b6f70d7bcb4b0527bedf0889cc0b4c3bb07db"}, + {file = "safetensors-0.4.4-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb276a53717f2bcfb6df0bcf284d8a12069002508d4c1ca715799226024ccd45"}, + {file = "safetensors-0.4.4-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a2c28c6487f17d8db0089e8b2cdc13de859366b94cc6cdc50e1b0a4147b56551"}, + {file = "safetensors-0.4.4-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:7915f0c60e4e6e65d90f136d85dd3b429ae9191c36b380e626064694563dbd9f"}, + {file = "safetensors-0.4.4-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:00eea99ae422fbfa0b46065acbc58b46bfafadfcec179d4b4a32d5c45006af6c"}, + {file = "safetensors-0.4.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:bb1ed4fcb0b3c2f3ea2c5767434622fe5d660e5752f21ac2e8d737b1e5e480bb"}, + {file = "safetensors-0.4.4-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:73fc9a0a4343188bdb421783e600bfaf81d0793cd4cce6bafb3c2ed567a74cd5"}, + {file = "safetensors-0.4.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c37e6b714200824c73ca6eaf007382de76f39466a46e97558b8dc4cf643cfbf"}, + {file = "safetensors-0.4.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f75698c5c5c542417ac4956acfc420f7d4a2396adca63a015fd66641ea751759"}, + {file = "safetensors-0.4.4-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ca1a209157f242eb183e209040097118472e169f2e069bfbd40c303e24866543"}, + {file = "safetensors-0.4.4-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:177f2b60a058f92a3cec7a1786c9106c29eca8987ecdfb79ee88126e5f47fa31"}, + {file = "safetensors-0.4.4-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ee9622e84fe6e4cd4f020e5fda70d6206feff3157731df7151d457fdae18e541"}, + {file = "safetensors-0.4.4.tar.gz", hash = "sha256:5fe3e9b705250d0172ed4e100a811543108653fb2b66b9e702a088ad03772a07"}, +] + +[package.extras] +all = ["safetensors[jax]", "safetensors[numpy]", "safetensors[paddlepaddle]", "safetensors[pinned-tf]", "safetensors[quality]", "safetensors[testing]", "safetensors[torch]"] +dev = ["safetensors[all]"] +jax = ["flax (>=0.6.3)", "jax (>=0.3.25)", "jaxlib (>=0.3.25)", "safetensors[numpy]"] +mlx = ["mlx (>=0.0.9)"] +numpy = ["numpy (>=1.21.6)"] +paddlepaddle = ["paddlepaddle (>=2.4.1)", "safetensors[numpy]"] +pinned-tf = ["safetensors[numpy]", "tensorflow (==2.11.0)"] +quality = ["black (==22.3)", "click (==8.0.4)", "flake8 (>=3.8.3)", "isort (>=5.5.4)"] +tensorflow = ["safetensors[numpy]", "tensorflow (>=2.11.0)"] +testing = ["h5py (>=3.7.0)", "huggingface-hub (>=0.12.1)", "hypothesis (>=6.70.2)", "pytest (>=7.2.0)", "pytest-benchmark (>=4.0.0)", "safetensors[numpy]", "setuptools-rust (>=1.5.2)"] +torch = ["safetensors[numpy]", "torch (>=1.10)"] + +[[package]] +name = "sentry-sdk" +version = "2.12.0" +description = "Python client for Sentry (https://sentry.io)" +optional = false +python-versions = ">=3.6" +files = [ + {file = "sentry_sdk-2.12.0-py2.py3-none-any.whl", hash = "sha256:7a8d5163d2ba5c5f4464628c6b68f85e86972f7c636acc78aed45c61b98b7a5e"}, + {file = "sentry_sdk-2.12.0.tar.gz", hash = "sha256:8763840497b817d44c49b3fe3f5f7388d083f2337ffedf008b2cdb63b5c86dc6"}, +] + +[package.dependencies] +certifi = "*" +urllib3 = ">=1.26.11" + +[package.extras] +aiohttp = ["aiohttp (>=3.5)"] +anthropic = ["anthropic (>=0.16)"] +arq = ["arq (>=0.23)"] +asyncpg = ["asyncpg (>=0.23)"] +beam = ["apache-beam (>=2.12)"] +bottle = ["bottle (>=0.12.13)"] +celery = ["celery (>=3)"] +celery-redbeat = ["celery-redbeat (>=2)"] +chalice = ["chalice (>=1.16.0)"] +clickhouse-driver = ["clickhouse-driver (>=0.2.0)"] +django = ["django (>=1.8)"] +falcon = ["falcon (>=1.4)"] +fastapi = ["fastapi (>=0.79.0)"] +flask = ["blinker (>=1.1)", "flask (>=0.11)", "markupsafe"] +grpcio = ["grpcio (>=1.21.1)", "protobuf (>=3.8.0)"] +httpx = ["httpx (>=0.16.0)"] +huey = ["huey (>=2)"] +huggingface-hub = ["huggingface-hub (>=0.22)"] +langchain = ["langchain (>=0.0.210)"] +loguru = ["loguru (>=0.5)"] +openai = ["openai (>=1.0.0)", "tiktoken (>=0.3.0)"] +opentelemetry = ["opentelemetry-distro (>=0.35b0)"] +opentelemetry-experimental = ["opentelemetry-distro"] +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 (>=6)"] + +[[package]] +name = "setproctitle" +version = "1.3.3" +description = "A Python module to customize the process title" +optional = false +python-versions = ">=3.7" +files = [ + {file = "setproctitle-1.3.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:897a73208da48db41e687225f355ce993167079eda1260ba5e13c4e53be7f754"}, + {file = "setproctitle-1.3.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8c331e91a14ba4076f88c29c777ad6b58639530ed5b24b5564b5ed2fd7a95452"}, + {file = "setproctitle-1.3.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbbd6c7de0771c84b4aa30e70b409565eb1fc13627a723ca6be774ed6b9d9fa3"}, + {file = "setproctitle-1.3.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c05ac48ef16ee013b8a326c63e4610e2430dbec037ec5c5b58fcced550382b74"}, + {file = "setproctitle-1.3.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1342f4fdb37f89d3e3c1c0a59d6ddbedbde838fff5c51178a7982993d238fe4f"}, + {file = "setproctitle-1.3.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc74e84fdfa96821580fb5e9c0b0777c1c4779434ce16d3d62a9c4d8c710df39"}, + {file = "setproctitle-1.3.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9617b676b95adb412bb69645d5b077d664b6882bb0d37bfdafbbb1b999568d85"}, + {file = "setproctitle-1.3.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6a249415f5bb88b5e9e8c4db47f609e0bf0e20a75e8d744ea787f3092ba1f2d0"}, + {file = "setproctitle-1.3.3-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:38da436a0aaace9add67b999eb6abe4b84397edf4a78ec28f264e5b4c9d53cd5"}, + {file = "setproctitle-1.3.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:da0d57edd4c95bf221b2ebbaa061e65b1788f1544977288bdf95831b6e44e44d"}, + {file = "setproctitle-1.3.3-cp310-cp310-win32.whl", hash = "sha256:a1fcac43918b836ace25f69b1dca8c9395253ad8152b625064415b1d2f9be4fb"}, + {file = "setproctitle-1.3.3-cp310-cp310-win_amd64.whl", hash = "sha256:200620c3b15388d7f3f97e0ae26599c0c378fdf07ae9ac5a13616e933cbd2086"}, + {file = "setproctitle-1.3.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:334f7ed39895d692f753a443102dd5fed180c571eb6a48b2a5b7f5b3564908c8"}, + {file = "setproctitle-1.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:950f6476d56ff7817a8fed4ab207727fc5260af83481b2a4b125f32844df513a"}, + {file = "setproctitle-1.3.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:195c961f54a09eb2acabbfc90c413955cf16c6e2f8caa2adbf2237d1019c7dd8"}, + {file = "setproctitle-1.3.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f05e66746bf9fe6a3397ec246fe481096664a9c97eb3fea6004735a4daf867fd"}, + {file = "setproctitle-1.3.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b5901a31012a40ec913265b64e48c2a4059278d9f4e6be628441482dd13fb8b5"}, + {file = "setproctitle-1.3.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64286f8a995f2cd934082b398fc63fca7d5ffe31f0e27e75b3ca6b4efda4e353"}, + {file = "setproctitle-1.3.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:184239903bbc6b813b1a8fc86394dc6ca7d20e2ebe6f69f716bec301e4b0199d"}, + {file = "setproctitle-1.3.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:664698ae0013f986118064b6676d7dcd28fefd0d7d5a5ae9497cbc10cba48fa5"}, + {file = "setproctitle-1.3.3-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:e5119a211c2e98ff18b9908ba62a3bd0e3fabb02a29277a7232a6fb4b2560aa0"}, + {file = "setproctitle-1.3.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:417de6b2e214e837827067048f61841f5d7fc27926f2e43954567094051aff18"}, + {file = "setproctitle-1.3.3-cp311-cp311-win32.whl", hash = "sha256:6a143b31d758296dc2f440175f6c8e0b5301ced3b0f477b84ca43cdcf7f2f476"}, + {file = "setproctitle-1.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:a680d62c399fa4b44899094027ec9a1bdaf6f31c650e44183b50d4c4d0ccc085"}, + {file = "setproctitle-1.3.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:d4460795a8a7a391e3567b902ec5bdf6c60a47d791c3b1d27080fc203d11c9dc"}, + {file = "setproctitle-1.3.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:bdfd7254745bb737ca1384dee57e6523651892f0ea2a7344490e9caefcc35e64"}, + {file = "setproctitle-1.3.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:477d3da48e216d7fc04bddab67b0dcde633e19f484a146fd2a34bb0e9dbb4a1e"}, + {file = "setproctitle-1.3.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ab2900d111e93aff5df9fddc64cf51ca4ef2c9f98702ce26524f1acc5a786ae7"}, + {file = "setproctitle-1.3.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:088b9efc62d5aa5d6edf6cba1cf0c81f4488b5ce1c0342a8b67ae39d64001120"}, + {file = "setproctitle-1.3.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a6d50252377db62d6a0bb82cc898089916457f2db2041e1d03ce7fadd4a07381"}, + {file = "setproctitle-1.3.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:87e668f9561fd3a457ba189edfc9e37709261287b52293c115ae3487a24b92f6"}, + {file = "setproctitle-1.3.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:287490eb90e7a0ddd22e74c89a92cc922389daa95babc833c08cf80c84c4df0a"}, + {file = "setproctitle-1.3.3-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:4fe1c49486109f72d502f8be569972e27f385fe632bd8895f4730df3c87d5ac8"}, + {file = "setproctitle-1.3.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4a6ba2494a6449b1f477bd3e67935c2b7b0274f2f6dcd0f7c6aceae10c6c6ba3"}, + {file = "setproctitle-1.3.3-cp312-cp312-win32.whl", hash = "sha256:2df2b67e4b1d7498632e18c56722851ba4db5d6a0c91aaf0fd395111e51cdcf4"}, + {file = "setproctitle-1.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:f38d48abc121263f3b62943f84cbaede05749047e428409c2c199664feb6abc7"}, + {file = "setproctitle-1.3.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:816330675e3504ae4d9a2185c46b573105d2310c20b19ea2b4596a9460a4f674"}, + {file = "setproctitle-1.3.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68f960bc22d8d8e4ac886d1e2e21ccbd283adcf3c43136161c1ba0fa509088e0"}, + {file = "setproctitle-1.3.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:00e6e7adff74796ef12753ff399491b8827f84f6c77659d71bd0b35870a17d8f"}, + {file = "setproctitle-1.3.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:53bc0d2358507596c22b02db079618451f3bd720755d88e3cccd840bafb4c41c"}, + {file = "setproctitle-1.3.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad6d20f9541f5f6ac63df553b6d7a04f313947f550eab6a61aa758b45f0d5657"}, + {file = "setproctitle-1.3.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c1c84beab776b0becaa368254801e57692ed749d935469ac10e2b9b825dbdd8e"}, + {file = "setproctitle-1.3.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:507e8dc2891021350eaea40a44ddd887c9f006e6b599af8d64a505c0f718f170"}, + {file = "setproctitle-1.3.3-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:b1067647ac7aba0b44b591936118a22847bda3c507b0a42d74272256a7a798e9"}, + {file = "setproctitle-1.3.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2e71f6365744bf53714e8bd2522b3c9c1d83f52ffa6324bd7cbb4da707312cd8"}, + {file = "setproctitle-1.3.3-cp37-cp37m-win32.whl", hash = "sha256:7f1d36a1e15a46e8ede4e953abb104fdbc0845a266ec0e99cc0492a4364f8c44"}, + {file = "setproctitle-1.3.3-cp37-cp37m-win_amd64.whl", hash = "sha256:c9a402881ec269d0cc9c354b149fc29f9ec1a1939a777f1c858cdb09c7a261df"}, + {file = "setproctitle-1.3.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ff814dea1e5c492a4980e3e7d094286077054e7ea116cbeda138819db194b2cd"}, + {file = "setproctitle-1.3.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:accb66d7b3ccb00d5cd11d8c6e07055a4568a24c95cf86109894dcc0c134cc89"}, + {file = "setproctitle-1.3.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:554eae5a5b28f02705b83a230e9d163d645c9a08914c0ad921df363a07cf39b1"}, + {file = "setproctitle-1.3.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a911b26264dbe9e8066c7531c0591cfab27b464459c74385b276fe487ca91c12"}, + {file = "setproctitle-1.3.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2982efe7640c4835f7355fdb4da313ad37fb3b40f5c69069912f8048f77b28c8"}, + {file = "setproctitle-1.3.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df3f4274b80709d8bcab2f9a862973d453b308b97a0b423a501bcd93582852e3"}, + {file = "setproctitle-1.3.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:af2c67ae4c795d1674a8d3ac1988676fa306bcfa1e23fddb5e0bd5f5635309ca"}, + {file = "setproctitle-1.3.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:af4061f67fd7ec01624c5e3c21f6b7af2ef0e6bab7fbb43f209e6506c9ce0092"}, + {file = "setproctitle-1.3.3-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:37a62cbe16d4c6294e84670b59cf7adcc73faafe6af07f8cb9adaf1f0e775b19"}, + {file = "setproctitle-1.3.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a83ca086fbb017f0d87f240a8f9bbcf0809f3b754ee01cec928fff926542c450"}, + {file = "setproctitle-1.3.3-cp38-cp38-win32.whl", hash = "sha256:059f4ce86f8cc92e5860abfc43a1dceb21137b26a02373618d88f6b4b86ba9b2"}, + {file = "setproctitle-1.3.3-cp38-cp38-win_amd64.whl", hash = "sha256:ab92e51cd4a218208efee4c6d37db7368fdf182f6e7ff148fb295ecddf264287"}, + {file = "setproctitle-1.3.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c7951820b77abe03d88b114b998867c0f99da03859e5ab2623d94690848d3e45"}, + {file = "setproctitle-1.3.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5bc94cf128676e8fac6503b37763adb378e2b6be1249d207630f83fc325d9b11"}, + {file = "setproctitle-1.3.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f5d9027eeda64d353cf21a3ceb74bb1760bd534526c9214e19f052424b37e42"}, + {file = "setproctitle-1.3.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2e4a8104db15d3462e29d9946f26bed817a5b1d7a47eabca2d9dc2b995991503"}, + {file = "setproctitle-1.3.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c32c41ace41f344d317399efff4cffb133e709cec2ef09c99e7a13e9f3b9483c"}, + {file = "setproctitle-1.3.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cbf16381c7bf7f963b58fb4daaa65684e10966ee14d26f5cc90f07049bfd8c1e"}, + {file = "setproctitle-1.3.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e18b7bd0898398cc97ce2dfc83bb192a13a087ef6b2d5a8a36460311cb09e775"}, + {file = "setproctitle-1.3.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:69d565d20efe527bd8a9b92e7f299ae5e73b6c0470f3719bd66f3cd821e0d5bd"}, + {file = "setproctitle-1.3.3-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:ddedd300cd690a3b06e7eac90ed4452348b1348635777ce23d460d913b5b63c3"}, + {file = "setproctitle-1.3.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:415bfcfd01d1fbf5cbd75004599ef167a533395955305f42220a585f64036081"}, + {file = "setproctitle-1.3.3-cp39-cp39-win32.whl", hash = "sha256:21112fcd2195d48f25760f0eafa7a76510871bbb3b750219310cf88b04456ae3"}, + {file = "setproctitle-1.3.3-cp39-cp39-win_amd64.whl", hash = "sha256:5a740f05d0968a5a17da3d676ce6afefebeeeb5ce137510901bf6306ba8ee002"}, + {file = "setproctitle-1.3.3-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:6b9e62ddb3db4b5205c0321dd69a406d8af9ee1693529d144e86bd43bcb4b6c0"}, + {file = "setproctitle-1.3.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9e3b99b338598de0bd6b2643bf8c343cf5ff70db3627af3ca427a5e1a1a90dd9"}, + {file = "setproctitle-1.3.3-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38ae9a02766dad331deb06855fb7a6ca15daea333b3967e214de12cfae8f0ef5"}, + {file = "setproctitle-1.3.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:200ede6fd11233085ba9b764eb055a2a191fb4ffb950c68675ac53c874c22e20"}, + {file = "setproctitle-1.3.3-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0d3a953c50776751e80fe755a380a64cb14d61e8762bd43041ab3f8cc436092f"}, + {file = "setproctitle-1.3.3-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e5e08e232b78ba3ac6bc0d23ce9e2bee8fad2be391b7e2da834fc9a45129eb87"}, + {file = "setproctitle-1.3.3-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1da82c3e11284da4fcbf54957dafbf0655d2389cd3d54e4eaba636faf6d117a"}, + {file = "setproctitle-1.3.3-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:aeaa71fb9568ebe9b911ddb490c644fbd2006e8c940f21cb9a1e9425bd709574"}, + {file = "setproctitle-1.3.3-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:59335d000c6250c35989394661eb6287187854e94ac79ea22315469ee4f4c244"}, + {file = "setproctitle-1.3.3-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c3ba57029c9c50ecaf0c92bb127224cc2ea9fda057b5d99d3f348c9ec2855ad3"}, + {file = "setproctitle-1.3.3-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d876d355c53d975c2ef9c4f2487c8f83dad6aeaaee1b6571453cb0ee992f55f6"}, + {file = "setproctitle-1.3.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:224602f0939e6fb9d5dd881be1229d485f3257b540f8a900d4271a2c2aa4e5f4"}, + {file = "setproctitle-1.3.3-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d7f27e0268af2d7503386e0e6be87fb9b6657afd96f5726b733837121146750d"}, + {file = "setproctitle-1.3.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f5e7266498cd31a4572378c61920af9f6b4676a73c299fce8ba93afd694f8ae7"}, + {file = "setproctitle-1.3.3-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33c5609ad51cd99d388e55651b19148ea99727516132fb44680e1f28dd0d1de9"}, + {file = "setproctitle-1.3.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:eae8988e78192fd1a3245a6f4f382390b61bce6cfcc93f3809726e4c885fa68d"}, + {file = "setproctitle-1.3.3.tar.gz", hash = "sha256:c913e151e7ea01567837ff037a23ca8740192880198b7fbb90b16d181607caae"}, +] + +[package.extras] +test = ["pytest"] + +[[package]] +name = "setuptools" +version = "72.1.0" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "setuptools-72.1.0-py3-none-any.whl", hash = "sha256:5a03e1860cf56bb6ef48ce186b0e557fdba433237481a9a625176c2831be15d1"}, + {file = "setuptools-72.1.0.tar.gz", hash = "sha256:8d243eff56d095e5817f796ede6ae32941278f542e0f941867cc05ae52b162ec"}, +] + +[package.extras] +core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.text (>=3.7)", "more-itertools (>=8.8)", "ordered-set (>=3.1.1)", "packaging (>=24)", "platformdirs (>=2.6.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "mypy (==1.11.*)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (<0.4)", "pytest-ruff (>=0.2.1)", "pytest-ruff (>=0.3.2)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +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.1" +description = "A pure Python implementation of a sliding window memory map manager" +optional = false +python-versions = ">=3.7" +files = [ + {file = "smmap-5.0.1-py3-none-any.whl", hash = "sha256:e6d8668fa5f93e706934a62d7b4db19c8d9eb8cf2adbb75ef1b675aa332b69da"}, + {file = "smmap-5.0.1.tar.gz", hash = "sha256:dceeb6c0028fdb6734471eb07c0cd2aae706ccaecab45965ee83f11c8d3b1f62"}, +] + +[[package]] +name = "sortedcontainers" +version = "2.4.0" +description = "Sorted Containers -- Sorted List, Sorted Dict, Sorted Set" +optional = false +python-versions = "*" +files = [ + {file = "sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0"}, + {file = "sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88"}, +] + +[[package]] +name = "stack-data" +version = "0.6.3" +description = "Extract data from python stack frames and tracebacks for informative displays" +optional = false +python-versions = "*" +files = [ + {file = "stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695"}, + {file = "stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9"}, +] + +[package.dependencies] +asttokens = ">=2.1.0" +executing = ">=1.2.0" +pure-eval = "*" + +[package.extras] +tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"] + +[[package]] +name = "submitit" +version = "1.5.1" +description = "\"Python 3.8+ toolbox for submitting jobs to Slurm" +optional = false +python-versions = ">=3.8" +files = [ + {file = "submitit-1.5.1-py3-none-any.whl", hash = "sha256:c62f3d50bf5070f89b94d79d83d1dca260eefd22a5b3a97eb1f3cd6a5a7b771c"}, + {file = "submitit-1.5.1.tar.gz", hash = "sha256:a08016daefb188420fb32412f627e2029a1a2d7484a55afe6b1892ff31928626"}, +] + +[package.dependencies] +cloudpickle = ">=1.2.1" +typing_extensions = ">=3.7.4.2" + +[package.extras] +dev = ["black (==23.3.0)", "coverage[toml] (>=5.1)", "flit (>=3.5.1)", "isort (==5.11.5)", "mypy (>=1.4.1)", "pre-commit (>=1.15.2)", "pylint (>=3.0.0)", "pytest (>=7.4.2)", "pytest-asyncio (>=0.15.0)", "pytest-cov (>=4.1.0)", "types-pkg_resources (>=0.1.2)"] + +[[package]] +name = "sympy" +version = "1.13.1" +description = "Computer algebra system (CAS) in Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "sympy-1.13.1-py3-none-any.whl", hash = "sha256:db36cdc64bf61b9b24578b6f7bab1ecdd2452cf008f34faa33776680c26d66f8"}, + {file = "sympy-1.13.1.tar.gz", hash = "sha256:9cebf7e04ff162015ce31c9c6c9144daa34a93bd082f54fd8f12deca4f47515f"}, +] + +[package.dependencies] +mpmath = ">=1.1.0,<1.4" + +[package.extras] +dev = ["hypothesis (>=6.70.0)", "pytest (>=7.1.0)"] + +[[package]] +name = "tabulate" +version = "0.9.0" +description = "Pretty-print tabular data" +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 = "tbb" +version = "2021.13.1" +description = "Intel® oneAPI Threading Building Blocks (oneTBB)" +optional = false +python-versions = "*" +files = [ + {file = "tbb-2021.13.1-py2.py3-none-manylinux1_i686.whl", hash = "sha256:bb5bdea0c0e9e6ad0739e7a8796c2635ce9eccca86dd48c426cd8027ac70fb1d"}, + {file = "tbb-2021.13.1-py2.py3-none-manylinux1_x86_64.whl", hash = "sha256:d916359dc685579d09e4b344241550afc1cc034f7f5ec7234c258b6680912d70"}, + {file = "tbb-2021.13.1-py3-none-win32.whl", hash = "sha256:00f5e5a70051650ddd0ab6247c0549521968339ec21002e475cd23b1cbf46d66"}, + {file = "tbb-2021.13.1-py3-none-win_amd64.whl", hash = "sha256:cbf024b2463fdab3ebe3fa6ff453026358e6b903839c80d647e08ad6d0796ee9"}, +] + +[[package]] +name = "termcolor" +version = "2.4.0" +description = "ANSI color formatting for output in terminal" +optional = false +python-versions = ">=3.8" +files = [ + {file = "termcolor-2.4.0-py3-none-any.whl", hash = "sha256:9297c0df9c99445c2412e832e882a7884038a25617c60cea2ad69488d4040d63"}, + {file = "termcolor-2.4.0.tar.gz", hash = "sha256:aab9e56047c8ac41ed798fa36d892a37aca6b3e9159f3e0c24bc64a9b3ac7b7a"}, +] + +[package.extras] +tests = ["pytest", "pytest-cov"] + +[[package]] +name = "timm" +version = "1.0.8" +description = "PyTorch Image Models" +optional = false +python-versions = ">=3.8" +files = [ + {file = "timm-1.0.8-py3-none-any.whl", hash = "sha256:2e4cf9e2224616fdb08e5f7a2972bd20e05f750236ea1f8dd53f3f326ceaee83"}, + {file = "timm-1.0.8.tar.gz", hash = "sha256:f54a579f1cc39c43d99a4b03603e39c4cee87d4f0a08aba9c22e19064b30bf95"}, +] + +[package.dependencies] +huggingface_hub = "*" +pyyaml = "*" +safetensors = "*" +torch = "*" +torchvision = "*" + +[[package]] +name = "tokenize-rt" +version = "6.0.0" +description = "A wrapper around the stdlib `tokenize` which roundtrips." +optional = false +python-versions = ">=3.8" +files = [ + {file = "tokenize_rt-6.0.0-py2.py3-none-any.whl", hash = "sha256:d4ff7ded2873512938b4f8cbb98c9b07118f01d30ac585a30d7a88353ca36d22"}, + {file = "tokenize_rt-6.0.0.tar.gz", hash = "sha256:b9711bdfc51210211137499b5e355d3de5ec88a85d2025c520cbb921b5194367"}, +] + +[[package]] +name = "tokenizers" +version = "0.19.1" +description = "" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tokenizers-0.19.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:952078130b3d101e05ecfc7fc3640282d74ed26bcf691400f872563fca15ac97"}, + {file = "tokenizers-0.19.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:82c8b8063de6c0468f08e82c4e198763e7b97aabfe573fd4cf7b33930ca4df77"}, + {file = "tokenizers-0.19.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f03727225feaf340ceeb7e00604825addef622d551cbd46b7b775ac834c1e1c4"}, + {file = "tokenizers-0.19.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:453e4422efdfc9c6b6bf2eae00d5e323f263fff62b29a8c9cd526c5003f3f642"}, + {file = "tokenizers-0.19.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:02e81bf089ebf0e7f4df34fa0207519f07e66d8491d963618252f2e0729e0b46"}, + {file = "tokenizers-0.19.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b07c538ba956843833fee1190cf769c60dc62e1cf934ed50d77d5502194d63b1"}, + {file = "tokenizers-0.19.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e28cab1582e0eec38b1f38c1c1fb2e56bce5dc180acb1724574fc5f47da2a4fe"}, + {file = "tokenizers-0.19.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b01afb7193d47439f091cd8f070a1ced347ad0f9144952a30a41836902fe09e"}, + {file = "tokenizers-0.19.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7fb297edec6c6841ab2e4e8f357209519188e4a59b557ea4fafcf4691d1b4c98"}, + {file = "tokenizers-0.19.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2e8a3dd055e515df7054378dc9d6fa8c8c34e1f32777fb9a01fea81496b3f9d3"}, + {file = "tokenizers-0.19.1-cp310-none-win32.whl", hash = "sha256:7ff898780a155ea053f5d934925f3902be2ed1f4d916461e1a93019cc7250837"}, + {file = "tokenizers-0.19.1-cp310-none-win_amd64.whl", hash = "sha256:bea6f9947e9419c2fda21ae6c32871e3d398cba549b93f4a65a2d369662d9403"}, + {file = "tokenizers-0.19.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:5c88d1481f1882c2e53e6bb06491e474e420d9ac7bdff172610c4f9ad3898059"}, + {file = "tokenizers-0.19.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ddf672ed719b4ed82b51499100f5417d7d9f6fb05a65e232249268f35de5ed14"}, + {file = "tokenizers-0.19.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:dadc509cc8a9fe460bd274c0e16ac4184d0958117cf026e0ea8b32b438171594"}, + {file = "tokenizers-0.19.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dfedf31824ca4915b511b03441784ff640378191918264268e6923da48104acc"}, + {file = "tokenizers-0.19.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac11016d0a04aa6487b1513a3a36e7bee7eec0e5d30057c9c0408067345c48d2"}, + {file = "tokenizers-0.19.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:76951121890fea8330d3a0df9a954b3f2a37e3ec20e5b0530e9a0044ca2e11fe"}, + {file = "tokenizers-0.19.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b342d2ce8fc8d00f376af068e3274e2e8649562e3bc6ae4a67784ded6b99428d"}, + {file = "tokenizers-0.19.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d16ff18907f4909dca9b076b9c2d899114dd6abceeb074eca0c93e2353f943aa"}, + {file = "tokenizers-0.19.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:706a37cc5332f85f26efbe2bdc9ef8a9b372b77e4645331a405073e4b3a8c1c6"}, + {file = "tokenizers-0.19.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:16baac68651701364b0289979ecec728546133e8e8fe38f66fe48ad07996b88b"}, + {file = "tokenizers-0.19.1-cp311-none-win32.whl", hash = "sha256:9ed240c56b4403e22b9584ee37d87b8bfa14865134e3e1c3fb4b2c42fafd3256"}, + {file = "tokenizers-0.19.1-cp311-none-win_amd64.whl", hash = "sha256:ad57d59341710b94a7d9dbea13f5c1e7d76fd8d9bcd944a7a6ab0b0da6e0cc66"}, + {file = "tokenizers-0.19.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:621d670e1b1c281a1c9698ed89451395d318802ff88d1fc1accff0867a06f153"}, + {file = "tokenizers-0.19.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d924204a3dbe50b75630bd16f821ebda6a5f729928df30f582fb5aade90c818a"}, + {file = "tokenizers-0.19.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:4f3fefdc0446b1a1e6d81cd4c07088ac015665d2e812f6dbba4a06267d1a2c95"}, + {file = "tokenizers-0.19.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9620b78e0b2d52ef07b0d428323fb34e8ea1219c5eac98c2596311f20f1f9266"}, + {file = "tokenizers-0.19.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:04ce49e82d100594715ac1b2ce87d1a36e61891a91de774755f743babcd0dd52"}, + {file = "tokenizers-0.19.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c5c2ff13d157afe413bf7e25789879dd463e5a4abfb529a2d8f8473d8042e28f"}, + {file = "tokenizers-0.19.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3174c76efd9d08f836bfccaca7cfec3f4d1c0a4cf3acbc7236ad577cc423c840"}, + {file = "tokenizers-0.19.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c9d5b6c0e7a1e979bec10ff960fae925e947aab95619a6fdb4c1d8ff3708ce3"}, + {file = "tokenizers-0.19.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a179856d1caee06577220ebcfa332af046d576fb73454b8f4d4b0ba8324423ea"}, + {file = "tokenizers-0.19.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:952b80dac1a6492170f8c2429bd11fcaa14377e097d12a1dbe0ef2fb2241e16c"}, + {file = "tokenizers-0.19.1-cp312-none-win32.whl", hash = "sha256:01d62812454c188306755c94755465505836fd616f75067abcae529c35edeb57"}, + {file = "tokenizers-0.19.1-cp312-none-win_amd64.whl", hash = "sha256:b70bfbe3a82d3e3fb2a5e9b22a39f8d1740c96c68b6ace0086b39074f08ab89a"}, + {file = "tokenizers-0.19.1-cp37-cp37m-macosx_10_12_x86_64.whl", hash = "sha256:bb9dfe7dae85bc6119d705a76dc068c062b8b575abe3595e3c6276480e67e3f1"}, + {file = "tokenizers-0.19.1-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:1f0360cbea28ea99944ac089c00de7b2e3e1c58f479fb8613b6d8d511ce98267"}, + {file = "tokenizers-0.19.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:71e3ec71f0e78780851fef28c2a9babe20270404c921b756d7c532d280349214"}, + {file = "tokenizers-0.19.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b82931fa619dbad979c0ee8e54dd5278acc418209cc897e42fac041f5366d626"}, + {file = "tokenizers-0.19.1-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e8ff5b90eabdcdaa19af697885f70fe0b714ce16709cf43d4952f1f85299e73a"}, + {file = "tokenizers-0.19.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e742d76ad84acbdb1a8e4694f915fe59ff6edc381c97d6dfdd054954e3478ad4"}, + {file = "tokenizers-0.19.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d8c5d59d7b59885eab559d5bc082b2985555a54cda04dda4c65528d90ad252ad"}, + {file = "tokenizers-0.19.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b2da5c32ed869bebd990c9420df49813709e953674c0722ff471a116d97b22d"}, + {file = "tokenizers-0.19.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:638e43936cc8b2cbb9f9d8dde0fe5e7e30766a3318d2342999ae27f68fdc9bd6"}, + {file = "tokenizers-0.19.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:78e769eb3b2c79687d9cb0f89ef77223e8e279b75c0a968e637ca7043a84463f"}, + {file = "tokenizers-0.19.1-cp37-none-win32.whl", hash = "sha256:72791f9bb1ca78e3ae525d4782e85272c63faaef9940d92142aa3eb79f3407a3"}, + {file = "tokenizers-0.19.1-cp37-none-win_amd64.whl", hash = "sha256:f3bbb7a0c5fcb692950b041ae11067ac54826204318922da754f908d95619fbc"}, + {file = "tokenizers-0.19.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:07f9295349bbbcedae8cefdbcfa7f686aa420be8aca5d4f7d1ae6016c128c0c5"}, + {file = "tokenizers-0.19.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:10a707cc6c4b6b183ec5dbfc5c34f3064e18cf62b4a938cb41699e33a99e03c1"}, + {file = "tokenizers-0.19.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6309271f57b397aa0aff0cbbe632ca9d70430839ca3178bf0f06f825924eca22"}, + {file = "tokenizers-0.19.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ad23d37d68cf00d54af184586d79b84075ada495e7c5c0f601f051b162112dc"}, + {file = "tokenizers-0.19.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:427c4f0f3df9109314d4f75b8d1f65d9477033e67ffaec4bca53293d3aca286d"}, + {file = "tokenizers-0.19.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e83a31c9cf181a0a3ef0abad2b5f6b43399faf5da7e696196ddd110d332519ee"}, + {file = "tokenizers-0.19.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c27b99889bd58b7e301468c0838c5ed75e60c66df0d4db80c08f43462f82e0d3"}, + {file = "tokenizers-0.19.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bac0b0eb952412b0b196ca7a40e7dce4ed6f6926489313414010f2e6b9ec2adf"}, + {file = "tokenizers-0.19.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8a6298bde623725ca31c9035a04bf2ef63208d266acd2bed8c2cb7d2b7d53ce6"}, + {file = "tokenizers-0.19.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:08a44864e42fa6d7d76d7be4bec62c9982f6f6248b4aa42f7302aa01e0abfd26"}, + {file = "tokenizers-0.19.1-cp38-none-win32.whl", hash = "sha256:1de5bc8652252d9357a666e609cb1453d4f8e160eb1fb2830ee369dd658e8975"}, + {file = "tokenizers-0.19.1-cp38-none-win_amd64.whl", hash = "sha256:0bcce02bf1ad9882345b34d5bd25ed4949a480cf0e656bbd468f4d8986f7a3f1"}, + {file = "tokenizers-0.19.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:0b9394bd204842a2a1fd37fe29935353742be4a3460b6ccbaefa93f58a8df43d"}, + {file = "tokenizers-0.19.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4692ab92f91b87769d950ca14dbb61f8a9ef36a62f94bad6c82cc84a51f76f6a"}, + {file = "tokenizers-0.19.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6258c2ef6f06259f70a682491c78561d492e885adeaf9f64f5389f78aa49a051"}, + {file = "tokenizers-0.19.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c85cf76561fbd01e0d9ea2d1cbe711a65400092bc52b5242b16cfd22e51f0c58"}, + {file = "tokenizers-0.19.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:670b802d4d82bbbb832ddb0d41df7015b3e549714c0e77f9bed3e74d42400fbe"}, + {file = "tokenizers-0.19.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:85aa3ab4b03d5e99fdd31660872249df5e855334b6c333e0bc13032ff4469c4a"}, + {file = "tokenizers-0.19.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cbf001afbbed111a79ca47d75941e9e5361297a87d186cbfc11ed45e30b5daba"}, + {file = "tokenizers-0.19.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4c89aa46c269e4e70c4d4f9d6bc644fcc39bb409cb2a81227923404dd6f5227"}, + {file = "tokenizers-0.19.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:39c1ec76ea1027438fafe16ecb0fb84795e62e9d643444c1090179e63808c69d"}, + {file = "tokenizers-0.19.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c2a0d47a89b48d7daa241e004e71fb5a50533718897a4cd6235cb846d511a478"}, + {file = "tokenizers-0.19.1-cp39-none-win32.whl", hash = "sha256:61b7fe8886f2e104d4caf9218b157b106207e0f2a4905c9c7ac98890688aabeb"}, + {file = "tokenizers-0.19.1-cp39-none-win_amd64.whl", hash = "sha256:f97660f6c43efd3e0bfd3f2e3e5615bf215680bad6ee3d469df6454b8c6e8256"}, + {file = "tokenizers-0.19.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3b11853f17b54c2fe47742c56d8a33bf49ce31caf531e87ac0d7d13d327c9334"}, + {file = "tokenizers-0.19.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d26194ef6c13302f446d39972aaa36a1dda6450bc8949f5eb4c27f51191375bd"}, + {file = "tokenizers-0.19.1-pp310-pypy310_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:e8d1ed93beda54bbd6131a2cb363a576eac746d5c26ba5b7556bc6f964425594"}, + {file = "tokenizers-0.19.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca407133536f19bdec44b3da117ef0d12e43f6d4b56ac4c765f37eca501c7bda"}, + {file = "tokenizers-0.19.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce05fde79d2bc2e46ac08aacbc142bead21614d937aac950be88dc79f9db9022"}, + {file = "tokenizers-0.19.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:35583cd46d16f07c054efd18b5d46af4a2f070a2dd0a47914e66f3ff5efb2b1e"}, + {file = "tokenizers-0.19.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:43350270bfc16b06ad3f6f07eab21f089adb835544417afda0f83256a8bf8b75"}, + {file = "tokenizers-0.19.1-pp37-pypy37_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b4399b59d1af5645bcee2072a463318114c39b8547437a7c2d6a186a1b5a0e2d"}, + {file = "tokenizers-0.19.1-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6852c5b2a853b8b0ddc5993cd4f33bfffdca4fcc5d52f89dd4b8eada99379285"}, + {file = "tokenizers-0.19.1-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bcd266ae85c3d39df2f7e7d0e07f6c41a55e9a3123bb11f854412952deacd828"}, + {file = "tokenizers-0.19.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ecb2651956eea2aa0a2d099434134b1b68f1c31f9a5084d6d53f08ed43d45ff2"}, + {file = "tokenizers-0.19.1-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:b279ab506ec4445166ac476fb4d3cc383accde1ea152998509a94d82547c8e2a"}, + {file = "tokenizers-0.19.1-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:89183e55fb86e61d848ff83753f64cded119f5d6e1f553d14ffee3700d0a4a49"}, + {file = "tokenizers-0.19.1-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b2edbc75744235eea94d595a8b70fe279dd42f3296f76d5a86dde1d46e35f574"}, + {file = "tokenizers-0.19.1-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:0e64bfde9a723274e9a71630c3e9494ed7b4c0f76a1faacf7fe294cd26f7ae7c"}, + {file = "tokenizers-0.19.1-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0b5ca92bfa717759c052e345770792d02d1f43b06f9e790ca0a1db62838816f3"}, + {file = "tokenizers-0.19.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f8a20266e695ec9d7a946a019c1d5ca4eddb6613d4f466888eee04f16eedb85"}, + {file = "tokenizers-0.19.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63c38f45d8f2a2ec0f3a20073cccb335b9f99f73b3c69483cd52ebc75369d8a1"}, + {file = "tokenizers-0.19.1-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:dd26e3afe8a7b61422df3176e06664503d3f5973b94f45d5c45987e1cb711876"}, + {file = "tokenizers-0.19.1-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:eddd5783a4a6309ce23432353cdb36220e25cbb779bfa9122320666508b44b88"}, + {file = "tokenizers-0.19.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:56ae39d4036b753994476a1b935584071093b55c7a72e3b8288e68c313ca26e7"}, + {file = "tokenizers-0.19.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:f9939ca7e58c2758c01b40324a59c034ce0cebad18e0d4563a9b1beab3018243"}, + {file = "tokenizers-0.19.1-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6c330c0eb815d212893c67a032e9dc1b38a803eccb32f3e8172c19cc69fbb439"}, + {file = "tokenizers-0.19.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec11802450a2487cdf0e634b750a04cbdc1c4d066b97d94ce7dd2cb51ebb325b"}, + {file = "tokenizers-0.19.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2b718f316b596f36e1dae097a7d5b91fc5b85e90bf08b01ff139bd8953b25af"}, + {file = "tokenizers-0.19.1-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:ed69af290c2b65169f0ba9034d1dc39a5db9459b32f1dd8b5f3f32a3fcf06eab"}, + {file = "tokenizers-0.19.1-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f8a9c828277133af13f3859d1b6bf1c3cb6e9e1637df0e45312e6b7c2e622b1f"}, + {file = "tokenizers-0.19.1.tar.gz", hash = "sha256:ee59e6680ed0fdbe6b724cf38bd70400a0c1dd623b07ac729087270caeac88e3"}, +] + +[package.dependencies] +huggingface-hub = ">=0.16.4,<1.0" + +[package.extras] +dev = ["tokenizers[testing]"] +docs = ["setuptools-rust", "sphinx", "sphinx-rtd-theme"] +testing = ["black (==22.3)", "datasets", "numpy", "pytest", "requests", "ruff"] + +[[package]] +name = "toml" +version = "0.10.2" +description = "Python Library for Tom's Obvious, Minimal Language" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, + {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, +] + +[[package]] +name = "tomli" +version = "2.0.1" +description = "A lil' TOML parser" +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.13.0" +description = "Style preserving TOML library" +optional = false +python-versions = ">=3.8" +files = [ + {file = "tomlkit-0.13.0-py3-none-any.whl", hash = "sha256:7075d3042d03b80f603482d69bf0c8f345c2b30e41699fd8883227f89972b264"}, + {file = "tomlkit-0.13.0.tar.gz", hash = "sha256:08ad192699734149f5b97b45f1f18dad7eb1b6d16bc72ad0c2335772650d7b72"}, +] + +[[package]] +name = "torch" +version = "2.3.1+cu121" +description = "Tensors and Dynamic neural networks in Python with strong GPU acceleration" +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "torch-2.3.1+cu121-cp310-cp310-linux_x86_64.whl", hash = "sha256:f0deb5d2f932a68ed54625ba140eddbf2af22be978ee19b9b63c986add6425b2"}, + {file = "torch-2.3.1+cu121-cp310-cp310-win_amd64.whl", hash = "sha256:bf1438aeb124fc36ae2d6b4b5c76d751d47a9fc3d7b15291b41f0caa8d5bf27b"}, + {file = "torch-2.3.1+cu121-cp311-cp311-linux_x86_64.whl", hash = "sha256:925e34af0905062a48b4f82d0e6656341ad4d626834a6a8245ef4eaee5375c98"}, + {file = "torch-2.3.1+cu121-cp311-cp311-win_amd64.whl", hash = "sha256:5a578516d0caf233993b3161d7dce1472bb917c59dd767c51921cd6696c3f3f7"}, + {file = "torch-2.3.1+cu121-cp312-cp312-linux_x86_64.whl", hash = "sha256:b3c586f4ab25e83efffccfb97079e91325329bc228166555c4bb93957753d4ea"}, + {file = "torch-2.3.1+cu121-cp312-cp312-win_amd64.whl", hash = "sha256:065a92a5ea2c89aad2bcd93e54c85c04a65c3e4a91cec2815e22c22706ec5183"}, + {file = "torch-2.3.1+cu121-cp38-cp38-linux_x86_64.whl", hash = "sha256:4e410f342fd86c73bea0ed245509d5ff5e6877bda54b249f75a33d535c877f2f"}, + {file = "torch-2.3.1+cu121-cp38-cp38-win_amd64.whl", hash = "sha256:c45c34c482fc20a32fa03511d3e66eb73d9dde0a1e6baffe9f8794d7d9cc6d04"}, + {file = "torch-2.3.1+cu121-cp39-cp39-linux_x86_64.whl", hash = "sha256:dfea610362c0e2a5ff28d322d6aa65d65e03e1334996119a5a3770c7d1821ac4"}, + {file = "torch-2.3.1+cu121-cp39-cp39-win_amd64.whl", hash = "sha256:b221b1534f1a20b5aab5fd547b782adaa0f1925d5421788e286eeaa0cbf6fd68"}, +] + +[package.dependencies] +filelock = "*" +fsspec = "*" +jinja2 = "*" +mkl = {version = ">=2021.1.1,<=2021.4.0", markers = "platform_system == \"Windows\""} +networkx = "*" +nvidia-cublas-cu12 = {version = "12.1.3.1", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-cuda-cupti-cu12 = {version = "12.1.105", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-cuda-nvrtc-cu12 = {version = "12.1.105", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-cuda-runtime-cu12 = {version = "12.1.105", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-cudnn-cu12 = {version = "8.9.2.26", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-cufft-cu12 = {version = "11.0.2.54", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-curand-cu12 = {version = "10.3.2.106", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-cusolver-cu12 = {version = "11.4.5.107", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-cusparse-cu12 = {version = "12.1.0.106", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-nccl-cu12 = {version = "2.20.5", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-nvtx-cu12 = {version = "12.1.105", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +sympy = "*" +triton = {version = "2.3.1", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version < \"3.12\""} +typing-extensions = ">=4.8.0" + +[package.extras] +opt-einsum = ["opt-einsum (>=3.3)"] +optree = ["optree (>=0.9.1)"] + +[package.source] +type = "legacy" +url = "https://download.pytorch.org/whl/cu121" +reference = "torch-cu121" + +[[package]] +name = "torchaudio" +version = "2.3.1+cu121" +description = "An audio package for PyTorch" +optional = false +python-versions = "*" +files = [ + {file = "torchaudio-2.3.1+cu121-cp310-cp310-linux_x86_64.whl", hash = "sha256:0b423f4ae3356f11f6723e8c77208ac3f9361a4f941e4cc08d86c32c137594bc"}, + {file = "torchaudio-2.3.1+cu121-cp310-cp310-win_amd64.whl", hash = "sha256:8b4017fda6eb9e840208696434f999a423b8c27bb54888a0e6a630b518b06855"}, + {file = "torchaudio-2.3.1+cu121-cp311-cp311-linux_x86_64.whl", hash = "sha256:2d9136bf3c9b6ab96dc42f26f6cb836d45d115f5766e16aff52835c8e7cde565"}, + {file = "torchaudio-2.3.1+cu121-cp311-cp311-win_amd64.whl", hash = "sha256:bc16945ceb5b24c627f72d5eabb6fa3cfeb8af98de41154c8f37aa63fb736f04"}, + {file = "torchaudio-2.3.1+cu121-cp312-cp312-linux_x86_64.whl", hash = "sha256:b00773dde3aa30d9b1b98875650428fbfb69fe30b98ebb5ceffbd8fc61de8125"}, + {file = "torchaudio-2.3.1+cu121-cp312-cp312-win_amd64.whl", hash = "sha256:917611fad28f6b28049e79c016e01fe604780789a68718a092b2ff43be43b060"}, + {file = "torchaudio-2.3.1+cu121-cp38-cp38-linux_x86_64.whl", hash = "sha256:e10a0fd1b351e849e7cd9e1e930dca3ffb8ca30f96239bea131f183f65945cdb"}, + {file = "torchaudio-2.3.1+cu121-cp38-cp38-win_amd64.whl", hash = "sha256:b4d1588bc27447b32c9ac9e3440ad072aa35c765cd45191befee7c2300e23e6b"}, + {file = "torchaudio-2.3.1+cu121-cp39-cp39-linux_x86_64.whl", hash = "sha256:6cebfc09d91ca403c048b9cf2ae3d64b4f54cf31a08584d0d3fbf7f9501ec0dc"}, + {file = "torchaudio-2.3.1+cu121-cp39-cp39-win_amd64.whl", hash = "sha256:9e3fbf260b29da0477c0b201e66d36d4f22203d25203989b3f6ad1e23ac68f21"}, +] + +[package.dependencies] +torch = "2.3.1" + +[package.source] +type = "legacy" +url = "https://download.pytorch.org/whl/cu121" +reference = "torch-cu121" + +[[package]] +name = "torchmetrics" +version = "1.4.1" +description = "PyTorch native Metrics" +optional = false +python-versions = ">=3.8" +files = [ + {file = "torchmetrics-1.4.1-py3-none-any.whl", hash = "sha256:c2e7cd56dd8bdc60ae63d712f3bdce649f23bd174d9180bdd0b746e0230b865a"}, +] + +[package.dependencies] +lightning-utilities = ">=0.8.0" +numpy = ">1.20.0" +packaging = ">17.1" +torch = ">=1.10.0" + +[package.extras] +all = ["SciencePlots (>=2.0.0)", "gammatone (>1.0.0)", "ipadic (>=1.0.0)", "matplotlib (>=3.3.0)", "mecab-python3 (>=1.0.6)", "mypy (==1.11.0)", "nltk (>=3.6)", "pesq (>=0.0.4)", "piq (<=0.8.0)", "pycocotools (>2.0.0)", "pystoi (>=0.3.0)", "regex (>=2021.9.24)", "scipy (>1.0.0)", "sentencepiece (>=0.2.0)", "torch (==2.3.1)", "torch-fidelity (<=0.4.0)", "torchaudio (>=0.10.0)", "torchvision (>=0.8)", "tqdm (>=4.41.0)", "transformers (>4.4.0)", "transformers (>=4.42.3)", "types-PyYAML", "types-emoji", "types-protobuf", "types-requests", "types-setuptools", "types-six", "types-tabulate"] +audio = ["gammatone (>1.0.0)", "pesq (>=0.0.4)", "pystoi (>=0.3.0)", "torchaudio (>=0.10.0)"] +detection = ["pycocotools (>2.0.0)", "torchvision (>=0.8)"] +dev = ["SciencePlots (>=2.0.0)", "bert-score (==0.3.13)", "dython (<=0.7.6)", "fairlearn", "fast-bss-eval (>=0.1.0)", "faster-coco-eval (>=1.3.3)", "gammatone (>1.0.0)", "huggingface-hub (<0.25)", "ipadic (>=1.0.0)", "jiwer (>=2.3.0)", "kornia (>=0.6.7)", "lpips (<=0.1.4)", "matplotlib (>=3.3.0)", "mecab-ko (>=1.0.0)", "mecab-ko-dic (>=1.0.0)", "mecab-python3 (>=1.0.6)", "mir-eval (>=0.6)", "monai (==1.3.2)", "mypy (==1.11.0)", "netcal (>1.0.0)", "nltk (>=3.6)", "numpy (<2.1.0)", "pandas (>1.0.0)", "pandas (>=1.4.0)", "pesq (>=0.0.4)", "piq (<=0.8.0)", "pycocotools (>2.0.0)", "pystoi (>=0.3.0)", "pytorch-msssim (==1.0.0)", "regex (>=2021.9.24)", "rouge-score (>0.1.0)", "sacrebleu (>=2.3.0)", "scikit-image (>=0.19.0)", "scipy (>1.0.0)", "sentencepiece (>=0.2.0)", "sewar (>=0.4.4)", "statsmodels (>0.13.5)", "torch (==2.3.1)", "torch-complex (<0.5.0)", "torch-fidelity (<=0.4.0)", "torchaudio (>=0.10.0)", "torchvision (>=0.8)", "tqdm (>=4.41.0)", "transformers (>4.4.0)", "transformers (>=4.42.3)", "types-PyYAML", "types-emoji", "types-protobuf", "types-requests", "types-setuptools", "types-six", "types-tabulate"] +image = ["scipy (>1.0.0)", "torch-fidelity (<=0.4.0)", "torchvision (>=0.8)"] +multimodal = ["piq (<=0.8.0)", "transformers (>=4.42.3)"] +text = ["ipadic (>=1.0.0)", "mecab-python3 (>=1.0.6)", "nltk (>=3.6)", "regex (>=2021.9.24)", "sentencepiece (>=0.2.0)", "tqdm (>=4.41.0)", "transformers (>4.4.0)"] +typing = ["mypy (==1.11.0)", "torch (==2.3.1)", "types-PyYAML", "types-emoji", "types-protobuf", "types-requests", "types-setuptools", "types-six", "types-tabulate"] +visual = ["SciencePlots (>=2.0.0)", "matplotlib (>=3.3.0)"] + +[[package]] +name = "torchvision" +version = "0.18.1+cu121" +description = "image and video datasets and models for torch deep learning" +optional = false +python-versions = ">=3.8" +files = [ + {file = "torchvision-0.18.1+cu121-cp310-cp310-linux_x86_64.whl", hash = "sha256:e95ba5a2c616939281e01babf11664d6d1725e81bba57ef81f81c3e57e4d4151"}, + {file = "torchvision-0.18.1+cu121-cp310-cp310-win_amd64.whl", hash = "sha256:fc2daccb9d290118fd706f42c280f4dcb5e2eb1e7e37b614f490dd548defe5b5"}, + {file = "torchvision-0.18.1+cu121-cp311-cp311-linux_x86_64.whl", hash = "sha256:2b2aec2c68e0ba17f9eed8921796fa2dbc7a493dea7a3b45d25c055ad4174868"}, + {file = "torchvision-0.18.1+cu121-cp311-cp311-win_amd64.whl", hash = "sha256:d85e21c03ab40b3676caaca4ec951a1f3a74ddcac3e68521c81f1869eb53ebf9"}, + {file = "torchvision-0.18.1+cu121-cp312-cp312-linux_x86_64.whl", hash = "sha256:ce8d5b992056f0640a39ef5734342e43ca4a801547de27fb8dbc3055d9345947"}, + {file = "torchvision-0.18.1+cu121-cp312-cp312-win_amd64.whl", hash = "sha256:4ef51349a1be161a60a8ad7e6f575917b13628e8255353f79c6a2c4d1432040f"}, + {file = "torchvision-0.18.1+cu121-cp38-cp38-linux_x86_64.whl", hash = "sha256:aee8961dcb8a418e92d06d4b3e9af52987293a48c14231c3c50c8eea3741e412"}, + {file = "torchvision-0.18.1+cu121-cp38-cp38-win_amd64.whl", hash = "sha256:54b167a0f8c17b568c0d7191aec45f77f6af4d9b0b8549e1857b34babbc5d9a6"}, + {file = "torchvision-0.18.1+cu121-cp39-cp39-linux_x86_64.whl", hash = "sha256:1ebf5dbbdf3af446c84e42baf2edbeb1bd6fb0cc9d3b4901af969c8391d14a5e"}, + {file = "torchvision-0.18.1+cu121-cp39-cp39-win_amd64.whl", hash = "sha256:15c3684dd265add6614a42b734e1b5a346e5a03ffa2414d2869cc01f9204b465"}, +] + +[package.dependencies] +numpy = "*" +pillow = ">=5.3.0,<8.3.dev0 || >=8.4.dev0" +torch = "2.3.1" + +[package.extras] +scipy = ["scipy"] + +[package.source] +type = "legacy" +url = "https://download.pytorch.org/whl/cu121" +reference = "torch-cu121" + +[[package]] +name = "tornado" +version = "6.4.1" +description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." +optional = false +python-versions = ">=3.8" +files = [ + {file = "tornado-6.4.1-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:163b0aafc8e23d8cdc3c9dfb24c5368af84a81e3364745ccb4427669bf84aec8"}, + {file = "tornado-6.4.1-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:6d5ce3437e18a2b66fbadb183c1d3364fb03f2be71299e7d10dbeeb69f4b2a14"}, + {file = "tornado-6.4.1-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2e20b9113cd7293f164dc46fffb13535266e713cdb87bd2d15ddb336e96cfc4"}, + {file = "tornado-6.4.1-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ae50a504a740365267b2a8d1a90c9fbc86b780a39170feca9bcc1787ff80842"}, + {file = "tornado-6.4.1-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:613bf4ddf5c7a95509218b149b555621497a6cc0d46ac341b30bd9ec19eac7f3"}, + {file = "tornado-6.4.1-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:25486eb223babe3eed4b8aecbac33b37e3dd6d776bc730ca14e1bf93888b979f"}, + {file = "tornado-6.4.1-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:454db8a7ecfcf2ff6042dde58404164d969b6f5d58b926da15e6b23817950fc4"}, + {file = "tornado-6.4.1-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a02a08cc7a9314b006f653ce40483b9b3c12cda222d6a46d4ac63bb6c9057698"}, + {file = "tornado-6.4.1-cp38-abi3-win32.whl", hash = "sha256:d9a566c40b89757c9aa8e6f032bcdb8ca8795d7c1a9762910c722b1635c9de4d"}, + {file = "tornado-6.4.1-cp38-abi3-win_amd64.whl", hash = "sha256:b24b8982ed444378d7f21d563f4180a2de31ced9d8d84443907a0a64da2072e7"}, + {file = "tornado-6.4.1.tar.gz", hash = "sha256:92d3ab53183d8c50f8204a51e6f91d18a15d5ef261e84d452800d4ff6fc504e9"}, +] + +[[package]] +name = "tqdm" +version = "4.66.5" +description = "Fast, Extensible Progress Meter" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tqdm-4.66.5-py3-none-any.whl", hash = "sha256:90279a3770753eafc9194a0364852159802111925aa30eb3f9d85b0e805ac7cd"}, + {file = "tqdm-4.66.5.tar.gz", hash = "sha256:e1020aef2e5096702d8a025ac7d16b1577279c9d63f8375b63083e9a5f0fcbad"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[package.extras] +dev = ["pytest (>=6)", "pytest-cov", "pytest-timeout", "pytest-xdist"] +notebook = ["ipywidgets (>=6)"] +slack = ["slack-sdk"] +telegram = ["requests"] + +[[package]] +name = "traitlets" +version = "5.14.3" +description = "Traitlets Python configuration system" +optional = false +python-versions = ">=3.8" +files = [ + {file = "traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f"}, + {file = "traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7"}, +] + +[package.extras] +docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"] +test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0,<8.2)", "pytest-mock", "pytest-mypy-testing"] + +[[package]] +name = "transformers" +version = "4.44.0" +description = "State-of-the-art Machine Learning for JAX, PyTorch and TensorFlow" +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "transformers-4.44.0-py3-none-any.whl", hash = "sha256:ea0ff72def71e9f4812d9414d4803b22681b1617aa6f511bd51cfff2b44a6fca"}, + {file = "transformers-4.44.0.tar.gz", hash = "sha256:75699495e30b7635ca444d8d372e138c687ab51a875b387e33f1fb759c37f196"}, +] + +[package.dependencies] +filelock = "*" +huggingface-hub = ">=0.23.2,<1.0" +numpy = ">=1.17" +packaging = ">=20.0" +pyyaml = ">=5.1" +regex = "!=2019.12.17" +requests = "*" +safetensors = ">=0.4.1" +tokenizers = ">=0.19,<0.20" +tqdm = ">=4.27" + +[package.extras] +accelerate = ["accelerate (>=0.21.0)"] +agents = ["Pillow (>=10.0.1,<=15.0)", "accelerate (>=0.21.0)", "datasets (!=2.5.0)", "diffusers", "opencv-python", "sentencepiece (>=0.1.91,!=0.1.92)", "torch"] +all = ["Pillow (>=10.0.1,<=15.0)", "accelerate (>=0.21.0)", "av (==9.2.0)", "codecarbon (==1.2.0)", "decord (==0.6.0)", "flax (>=0.4.1,<=0.7.0)", "jax (>=0.4.1,<=0.4.13)", "jaxlib (>=0.4.1,<=0.4.13)", "kenlm", "keras-nlp (>=0.3.1,<0.14.0)", "librosa", "onnxconverter-common", "optax (>=0.0.8,<=0.1.4)", "optuna", "phonemizer", "protobuf", "pyctcdecode (>=0.4.0)", "ray[tune] (>=2.7.0)", "scipy (<1.13.0)", "sentencepiece (>=0.1.91,!=0.1.92)", "sigopt", "tensorflow (>2.9,<2.16)", "tensorflow-text (<2.16)", "tf2onnx", "timm (<=0.9.16)", "tokenizers (>=0.19,<0.20)", "torch", "torchaudio", "torchvision"] +audio = ["kenlm", "librosa", "phonemizer", "pyctcdecode (>=0.4.0)"] +benchmark = ["optimum-benchmark (>=0.2.0)"] +codecarbon = ["codecarbon (==1.2.0)"] +deepspeed = ["accelerate (>=0.21.0)", "deepspeed (>=0.9.3)"] +deepspeed-testing = ["GitPython (<3.1.19)", "accelerate (>=0.21.0)", "beautifulsoup4", "cookiecutter (==1.7.3)", "datasets (!=2.5.0)", "deepspeed (>=0.9.3)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "nltk", "optuna", "parameterized", "protobuf", "psutil", "pydantic", "pytest (>=7.2.0,<8.0.0)", "pytest-rich", "pytest-timeout", "pytest-xdist", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (==0.5.1)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "sentencepiece (>=0.1.91,!=0.1.92)", "tensorboard", "timeout-decorator"] +dev = ["GitPython (<3.1.19)", "Pillow (>=10.0.1,<=15.0)", "accelerate (>=0.21.0)", "av (==9.2.0)", "beautifulsoup4", "codecarbon (==1.2.0)", "cookiecutter (==1.7.3)", "datasets (!=2.5.0)", "decord (==0.6.0)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "flax (>=0.4.1,<=0.7.0)", "fugashi (>=1.0)", "ipadic (>=1.0.0,<2.0)", "isort (>=5.5.4)", "jax (>=0.4.1,<=0.4.13)", "jaxlib (>=0.4.1,<=0.4.13)", "kenlm", "keras-nlp (>=0.3.1,<0.14.0)", "librosa", "nltk", "onnxconverter-common", "optax (>=0.0.8,<=0.1.4)", "optuna", "parameterized", "phonemizer", "protobuf", "psutil", "pyctcdecode (>=0.4.0)", "pydantic", "pytest (>=7.2.0,<8.0.0)", "pytest-rich", "pytest-timeout", "pytest-xdist", "ray[tune] (>=2.7.0)", "rhoknp (>=1.1.0,<1.3.1)", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (==0.5.1)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "scikit-learn", "scipy (<1.13.0)", "sentencepiece (>=0.1.91,!=0.1.92)", "sigopt", "sudachidict-core (>=20220729)", "sudachipy (>=0.6.6)", "tensorboard", "tensorflow (>2.9,<2.16)", "tensorflow-text (<2.16)", "tf2onnx", "timeout-decorator", "timm (<=0.9.16)", "tokenizers (>=0.19,<0.20)", "torch", "torchaudio", "torchvision", "unidic (>=1.0.2)", "unidic-lite (>=1.0.7)", "urllib3 (<2.0.0)"] +dev-tensorflow = ["GitPython (<3.1.19)", "Pillow (>=10.0.1,<=15.0)", "beautifulsoup4", "cookiecutter (==1.7.3)", "datasets (!=2.5.0)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "isort (>=5.5.4)", "kenlm", "keras-nlp (>=0.3.1,<0.14.0)", "librosa", "nltk", "onnxconverter-common", "onnxruntime (>=1.4.0)", "onnxruntime-tools (>=1.4.2)", "parameterized", "phonemizer", "protobuf", "psutil", "pyctcdecode (>=0.4.0)", "pydantic", "pytest (>=7.2.0,<8.0.0)", "pytest-rich", "pytest-timeout", "pytest-xdist", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (==0.5.1)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "scikit-learn", "sentencepiece (>=0.1.91,!=0.1.92)", "tensorboard", "tensorflow (>2.9,<2.16)", "tensorflow-text (<2.16)", "tf2onnx", "timeout-decorator", "tokenizers (>=0.19,<0.20)", "urllib3 (<2.0.0)"] +dev-torch = ["GitPython (<3.1.19)", "Pillow (>=10.0.1,<=15.0)", "accelerate (>=0.21.0)", "beautifulsoup4", "codecarbon (==1.2.0)", "cookiecutter (==1.7.3)", "datasets (!=2.5.0)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "fugashi (>=1.0)", "ipadic (>=1.0.0,<2.0)", "isort (>=5.5.4)", "kenlm", "librosa", "nltk", "onnxruntime (>=1.4.0)", "onnxruntime-tools (>=1.4.2)", "optuna", "parameterized", "phonemizer", "protobuf", "psutil", "pyctcdecode (>=0.4.0)", "pydantic", "pytest (>=7.2.0,<8.0.0)", "pytest-rich", "pytest-timeout", "pytest-xdist", "ray[tune] (>=2.7.0)", "rhoknp (>=1.1.0,<1.3.1)", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (==0.5.1)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "scikit-learn", "sentencepiece (>=0.1.91,!=0.1.92)", "sigopt", "sudachidict-core (>=20220729)", "sudachipy (>=0.6.6)", "tensorboard", "timeout-decorator", "timm (<=0.9.16)", "tokenizers (>=0.19,<0.20)", "torch", "torchaudio", "torchvision", "unidic (>=1.0.2)", "unidic-lite (>=1.0.7)", "urllib3 (<2.0.0)"] +flax = ["flax (>=0.4.1,<=0.7.0)", "jax (>=0.4.1,<=0.4.13)", "jaxlib (>=0.4.1,<=0.4.13)", "optax (>=0.0.8,<=0.1.4)", "scipy (<1.13.0)"] +flax-speech = ["kenlm", "librosa", "phonemizer", "pyctcdecode (>=0.4.0)"] +ftfy = ["ftfy"] +integrations = ["optuna", "ray[tune] (>=2.7.0)", "sigopt"] +ja = ["fugashi (>=1.0)", "ipadic (>=1.0.0,<2.0)", "rhoknp (>=1.1.0,<1.3.1)", "sudachidict-core (>=20220729)", "sudachipy (>=0.6.6)", "unidic (>=1.0.2)", "unidic-lite (>=1.0.7)"] +modelcreation = ["cookiecutter (==1.7.3)"] +natten = ["natten (>=0.14.6,<0.15.0)"] +onnx = ["onnxconverter-common", "onnxruntime (>=1.4.0)", "onnxruntime-tools (>=1.4.2)", "tf2onnx"] +onnxruntime = ["onnxruntime (>=1.4.0)", "onnxruntime-tools (>=1.4.2)"] +optuna = ["optuna"] +quality = ["GitPython (<3.1.19)", "datasets (!=2.5.0)", "isort (>=5.5.4)", "ruff (==0.5.1)", "urllib3 (<2.0.0)"] +ray = ["ray[tune] (>=2.7.0)"] +retrieval = ["datasets (!=2.5.0)", "faiss-cpu"] +ruff = ["ruff (==0.5.1)"] +sagemaker = ["sagemaker (>=2.31.0)"] +sentencepiece = ["protobuf", "sentencepiece (>=0.1.91,!=0.1.92)"] +serving = ["fastapi", "pydantic", "starlette", "uvicorn"] +sigopt = ["sigopt"] +sklearn = ["scikit-learn"] +speech = ["kenlm", "librosa", "phonemizer", "pyctcdecode (>=0.4.0)", "torchaudio"] +testing = ["GitPython (<3.1.19)", "beautifulsoup4", "cookiecutter (==1.7.3)", "datasets (!=2.5.0)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "nltk", "parameterized", "psutil", "pydantic", "pytest (>=7.2.0,<8.0.0)", "pytest-rich", "pytest-timeout", "pytest-xdist", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (==0.5.1)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "sentencepiece (>=0.1.91,!=0.1.92)", "tensorboard", "timeout-decorator"] +tf = ["keras-nlp (>=0.3.1,<0.14.0)", "onnxconverter-common", "tensorflow (>2.9,<2.16)", "tensorflow-text (<2.16)", "tf2onnx"] +tf-cpu = ["keras (>2.9,<2.16)", "keras-nlp (>=0.3.1,<0.14.0)", "onnxconverter-common", "tensorflow-cpu (>2.9,<2.16)", "tensorflow-probability (<0.24)", "tensorflow-text (<2.16)", "tf2onnx"] +tf-speech = ["kenlm", "librosa", "phonemizer", "pyctcdecode (>=0.4.0)"] +timm = ["timm (<=0.9.16)"] +tokenizers = ["tokenizers (>=0.19,<0.20)"] +torch = ["accelerate (>=0.21.0)", "torch"] +torch-speech = ["kenlm", "librosa", "phonemizer", "pyctcdecode (>=0.4.0)", "torchaudio"] +torch-vision = ["Pillow (>=10.0.1,<=15.0)", "torchvision"] +torchhub = ["filelock", "huggingface-hub (>=0.23.2,<1.0)", "importlib-metadata", "numpy (>=1.17)", "packaging (>=20.0)", "protobuf", "regex (!=2019.12.17)", "requests", "sentencepiece (>=0.1.91,!=0.1.92)", "tokenizers (>=0.19,<0.20)", "torch", "tqdm (>=4.27)"] +video = ["av (==9.2.0)", "decord (==0.6.0)"] +vision = ["Pillow (>=10.0.1,<=15.0)"] + +[[package]] +name = "triton" +version = "2.3.1" +description = "A language and compiler for custom Deep Learning operations" +optional = false +python-versions = "*" +files = [ + {file = "triton-2.3.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c84595cbe5e546b1b290d2a58b1494df5a2ef066dd890655e5b8a8a92205c33"}, + {file = "triton-2.3.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9d64ae33bcb3a7a18081e3a746e8cf87ca8623ca13d2c362413ce7a486f893e"}, + {file = "triton-2.3.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eaf80e8761a9e3498aa92e7bf83a085b31959c61f5e8ac14eedd018df6fccd10"}, + {file = "triton-2.3.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b13bf35a2b659af7159bf78e92798dc62d877aa991de723937329e2d382f1991"}, + {file = "triton-2.3.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63381e35ded3304704ea867ffde3b7cfc42c16a55b3062d41e017ef510433d66"}, + {file = "triton-2.3.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1d968264523c7a07911c8fb51b4e0d1b920204dae71491b1fe7b01b62a31e124"}, +] + +[package.dependencies] +filelock = "*" + +[package.extras] +build = ["cmake (>=3.20)", "lit"] +tests = ["autopep8", "flake8", "isort", "numpy", "pytest", "scipy (>=1.7.1)", "torch"] +tutorials = ["matplotlib", "pandas", "tabulate", "torch"] + +[[package]] +name = "typing-extensions" +version = "4.12.2" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, + {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, +] + +[[package]] +name = "typos" +version = "1.23.6" +description = "Source Code Spelling Correction" +optional = false +python-versions = ">=3.7" +files = [ + {file = "typos-1.23.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:9209947ab1e815bcb8cb781fc73fd6ad88eacdea7b1c15e73ca49217fa7c44e7"}, + {file = "typos-1.23.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:b049bfce407d7d61c5be4955d2fae6db644dc5d56ca236224cae0c3978024a75"}, + {file = "typos-1.23.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0b17e19c5e6b4f46acf0f60d053e0c188d31c09748f487f171465623f5f3380"}, + {file = "typos-1.23.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b609d525078b222cf8e25bd8e5cd60a56a542129d7bccb4f6cc992f686410331"}, + {file = "typos-1.23.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4fbf955dc4a09a95d3358f8edb10c1418e45bf07a6c9c414432320009a74dd5f"}, + {file = "typos-1.23.6-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:c686b06039b7fd95eed661cd2093fa7f048c76cb40b6bad55827a68aa707240a"}, + {file = "typos-1.23.6-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:0fda8c8502bce101277eb0a4b4d04847fc7018e2f9cff6d2fc86b3fdec239755"}, + {file = "typos-1.23.6-py3-none-win32.whl", hash = "sha256:8edaba24813be7ef678868e8ed49c48eb70cf128afc41ae86cc2127fb32e326b"}, + {file = "typos-1.23.6-py3-none-win_amd64.whl", hash = "sha256:d47b7d0e08975adf67873a8e43dc09fc1b6ff655a4241497348808ee54442668"}, + {file = "typos-1.23.6.tar.gz", hash = "sha256:2691988d2a15cde2cdd4f2fa5fd32880765b2a68ed6ccd48d6dc693c44447bcf"}, +] + +[[package]] +name = "tzdata" +version = "2024.1" +description = "Provider of IANA time zone data" +optional = false +python-versions = ">=2" +files = [ + {file = "tzdata-2024.1-py2.py3-none-any.whl", hash = "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252"}, + {file = "tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd"}, +] + +[[package]] +name = "urllib3" +version = "2.2.2" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.8" +files = [ + {file = "urllib3-2.2.2-py3-none-any.whl", hash = "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472"}, + {file = "urllib3-2.2.2.tar.gz", hash = "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +h2 = ["h2 (>=4,<5)"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] + +[[package]] +name = "virtualenv" +version = "20.26.3" +description = "Virtual Python Environment builder" +optional = false +python-versions = ">=3.7" +files = [ + {file = "virtualenv-20.26.3-py3-none-any.whl", hash = "sha256:8cc4a31139e796e9a7de2cd5cf2489de1217193116a8fd42328f1bd65f434589"}, + {file = "virtualenv-20.26.3.tar.gz", hash = "sha256:4c43a2a236279d9ea36a0d76f98d84bd6ca94ac4e0f4a3b9d46d05e10fea542a"}, +] + +[package.dependencies] +distlib = ">=0.3.7,<1" +filelock = ">=3.12.2,<4" +platformdirs = ">=3.9.1,<5" + +[package.extras] +docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] +test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] + +[[package]] +name = "wandb" +version = "0.17.6" +description = "A CLI and library for interacting with the Weights & Biases API." +optional = false +python-versions = ">=3.7" +files = [ + {file = "wandb-0.17.6-py3-none-any.whl", hash = "sha256:30c4f110d406368a4653fa137ad04e8ba0a68dee25836de55c3ca384b23e30c2"}, + {file = "wandb-0.17.6-py3-none-macosx_10_14_x86_64.whl", hash = "sha256:c554897b9b9a38490fa86d972975528b424daf5bcf35a5249ef2d25bf99986c2"}, + {file = "wandb-0.17.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:4015215335b4cff6268342339022382a38a7e086a8388c8b52df53bc6f18f39a"}, + {file = "wandb-0.17.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d38c52fdd0add655e0c3d408d7d0a84a19ec559512b157b8d42e9f67d98b3004"}, + {file = "wandb-0.17.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86743223a5f0c76a5c5ebc5a077aaa87221545ff8f12ba754c51a0bc1fe1e67e"}, + {file = "wandb-0.17.6-py3-none-win32.whl", hash = "sha256:247b1c9677fd633a460201f421d4fd4f370e7243d06257fab0ad1bb728ddcc1c"}, + {file = "wandb-0.17.6-py3-none-win_amd64.whl", hash = "sha256:51954f993b372c20812616838302183f0e3abf137614f05d80c7c17c307bfff9"}, + {file = "wandb-0.17.6.tar.gz", hash = "sha256:416739f293d59b95bcb189ecbaeb19b3a74ff2b80d501a90b93e6b6f9435c017"}, +] + +[package.dependencies] +click = ">=7.1,<8.0.0 || >8.0.0" +docker-pycreds = ">=0.4.0" +gitpython = ">=1.0.0,<3.1.29 || >3.1.29" +platformdirs = "*" +protobuf = [ + {version = ">=3.15.0,<4.21.0 || >4.21.0,<6", markers = "python_version == \"3.9\" and sys_platform == \"linux\""}, + {version = ">=3.19.0,<4.21.0 || >4.21.0,<6", 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 = "*" +typing-extensions = {version = "*", markers = "python_version < \"3.10\""} + +[package.extras] +aws = ["boto3"] +azure = ["azure-identity", "azure-storage-blob"] +gcp = ["google-cloud-storage"] +importers = ["filelock", "mlflow", "polars (<=1.2.1)", "rich", "tenacity"] +kubeflow = ["google-cloud-storage", "kubernetes", "minio", "sh"] +launch = ["awscli", "azure-containerregistry", "azure-identity", "azure-storage-blob", "boto3", "botocore", "chardet", "google-auth", "google-cloud-aiplatform", "google-cloud-artifact-registry", "google-cloud-compute", "google-cloud-storage", "iso8601", "jsonschema", "kubernetes", "kubernetes-asyncio", "nbconvert", "nbformat", "optuna", "pydantic", "pyyaml (>=6.0.0)", "tomli", "typing-extensions"] +media = ["bokeh", "moviepy", "numpy", "pillow", "plotly (>=5.18.0)", "rdkit-pypi", "soundfile"] +models = ["cloudpickle"] +perf = ["orjson"] +sweeps = ["sweeps (>=0.2.0)"] +workspaces = ["wandb-workspaces"] + +[[package]] +name = "wcwidth" +version = "0.2.13" +description = "Measures the displayed width of unicode strings in a terminal" +optional = false +python-versions = "*" +files = [ + {file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"}, + {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"}, +] + +[[package]] +name = "webencodings" +version = "0.5.1" +description = "Character encoding aliases for legacy web content" +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 = "yacs" +version = "0.1.8" +description = "Yet Another Configuration System" +optional = false +python-versions = "*" +files = [ + {file = "yacs-0.1.8-py2-none-any.whl", hash = "sha256:d43d1854c1ffc4634c5b349d1c1120f86f05c3a294c9d141134f282961ab5d94"}, + {file = "yacs-0.1.8-py3-none-any.whl", hash = "sha256:99f893e30497a4b66842821bac316386f7bd5c4f47ad35c9073ef089aa33af32"}, + {file = "yacs-0.1.8.tar.gz", hash = "sha256:efc4c732942b3103bea904ee89af98bcd27d01f0ac12d8d4d369f1e7a2914384"}, +] + +[package.dependencies] +PyYAML = "*" + +[[package]] +name = "yarl" +version = "1.9.4" +description = "Yet another URL library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "yarl-1.9.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a8c1df72eb746f4136fe9a2e72b0c9dc1da1cbd23b5372f94b5820ff8ae30e0e"}, + {file = "yarl-1.9.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a3a6ed1d525bfb91b3fc9b690c5a21bb52de28c018530ad85093cc488bee2dd2"}, + {file = "yarl-1.9.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c38c9ddb6103ceae4e4498f9c08fac9b590c5c71b0370f98714768e22ac6fa66"}, + {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d9e09c9d74f4566e905a0b8fa668c58109f7624db96a2171f21747abc7524234"}, + {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8477c1ee4bd47c57d49621a062121c3023609f7a13b8a46953eb6c9716ca392"}, + {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5ff2c858f5f6a42c2a8e751100f237c5e869cbde669a724f2062d4c4ef93551"}, + {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:357495293086c5b6d34ca9616a43d329317feab7917518bc97a08f9e55648455"}, + {file = "yarl-1.9.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54525ae423d7b7a8ee81ba189f131054defdb122cde31ff17477951464c1691c"}, + {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:801e9264d19643548651b9db361ce3287176671fb0117f96b5ac0ee1c3530d53"}, + {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e516dc8baf7b380e6c1c26792610230f37147bb754d6426462ab115a02944385"}, + {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:7d5aaac37d19b2904bb9dfe12cdb08c8443e7ba7d2852894ad448d4b8f442863"}, + {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:54beabb809ffcacbd9d28ac57b0db46e42a6e341a030293fb3185c409e626b8b"}, + {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bac8d525a8dbc2a1507ec731d2867025d11ceadcb4dd421423a5d42c56818541"}, + {file = "yarl-1.9.4-cp310-cp310-win32.whl", hash = "sha256:7855426dfbddac81896b6e533ebefc0af2f132d4a47340cee6d22cac7190022d"}, + {file = "yarl-1.9.4-cp310-cp310-win_amd64.whl", hash = "sha256:848cd2a1df56ddbffeb375535fb62c9d1645dde33ca4d51341378b3f5954429b"}, + {file = "yarl-1.9.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:35a2b9396879ce32754bd457d31a51ff0a9d426fd9e0e3c33394bf4b9036b099"}, + {file = "yarl-1.9.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c7d56b293cc071e82532f70adcbd8b61909eec973ae9d2d1f9b233f3d943f2c"}, + {file = "yarl-1.9.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d8a1c6c0be645c745a081c192e747c5de06e944a0d21245f4cf7c05e457c36e0"}, + {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b3c1ffe10069f655ea2d731808e76e0f452fc6c749bea04781daf18e6039525"}, + {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:549d19c84c55d11687ddbd47eeb348a89df9cb30e1993f1b128f4685cd0ebbf8"}, + {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7409f968456111140c1c95301cadf071bd30a81cbd7ab829169fb9e3d72eae9"}, + {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e23a6d84d9d1738dbc6e38167776107e63307dfc8ad108e580548d1f2c587f42"}, + {file = "yarl-1.9.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d8b889777de69897406c9fb0b76cdf2fd0f31267861ae7501d93003d55f54fbe"}, + {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:03caa9507d3d3c83bca08650678e25364e1843b484f19986a527630ca376ecce"}, + {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4e9035df8d0880b2f1c7f5031f33f69e071dfe72ee9310cfc76f7b605958ceb9"}, + {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:c0ec0ed476f77db9fb29bca17f0a8fcc7bc97ad4c6c1d8959c507decb22e8572"}, + {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:ee04010f26d5102399bd17f8df8bc38dc7ccd7701dc77f4a68c5b8d733406958"}, + {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:49a180c2e0743d5d6e0b4d1a9e5f633c62eca3f8a86ba5dd3c471060e352ca98"}, + {file = "yarl-1.9.4-cp311-cp311-win32.whl", hash = "sha256:81eb57278deb6098a5b62e88ad8281b2ba09f2f1147c4767522353eaa6260b31"}, + {file = "yarl-1.9.4-cp311-cp311-win_amd64.whl", hash = "sha256:d1d2532b340b692880261c15aee4dc94dd22ca5d61b9db9a8a361953d36410b1"}, + {file = "yarl-1.9.4-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0d2454f0aef65ea81037759be5ca9947539667eecebca092733b2eb43c965a81"}, + {file = "yarl-1.9.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:44d8ffbb9c06e5a7f529f38f53eda23e50d1ed33c6c869e01481d3fafa6b8142"}, + {file = "yarl-1.9.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:aaaea1e536f98754a6e5c56091baa1b6ce2f2700cc4a00b0d49eca8dea471074"}, + {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3777ce5536d17989c91696db1d459574e9a9bd37660ea7ee4d3344579bb6f129"}, + {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fc5fc1eeb029757349ad26bbc5880557389a03fa6ada41703db5e068881e5f2"}, + {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea65804b5dc88dacd4a40279af0cdadcfe74b3e5b4c897aa0d81cf86927fee78"}, + {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa102d6d280a5455ad6a0f9e6d769989638718e938a6a0a2ff3f4a7ff8c62cc4"}, + {file = "yarl-1.9.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09efe4615ada057ba2d30df871d2f668af661e971dfeedf0c159927d48bbeff0"}, + {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:008d3e808d03ef28542372d01057fd09168419cdc8f848efe2804f894ae03e51"}, + {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:6f5cb257bc2ec58f437da2b37a8cd48f666db96d47b8a3115c29f316313654ff"}, + {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:992f18e0ea248ee03b5a6e8b3b4738850ae7dbb172cc41c966462801cbf62cf7"}, + {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:0e9d124c191d5b881060a9e5060627694c3bdd1fe24c5eecc8d5d7d0eb6faabc"}, + {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3986b6f41ad22988e53d5778f91855dc0399b043fc8946d4f2e68af22ee9ff10"}, + {file = "yarl-1.9.4-cp312-cp312-win32.whl", hash = "sha256:4b21516d181cd77ebd06ce160ef8cc2a5e9ad35fb1c5930882baff5ac865eee7"}, + {file = "yarl-1.9.4-cp312-cp312-win_amd64.whl", hash = "sha256:a9bd00dc3bc395a662900f33f74feb3e757429e545d831eef5bb280252631984"}, + {file = "yarl-1.9.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:63b20738b5aac74e239622d2fe30df4fca4942a86e31bf47a81a0e94c14df94f"}, + {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7d7f7de27b8944f1fee2c26a88b4dabc2409d2fea7a9ed3df79b67277644e17"}, + {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c74018551e31269d56fab81a728f683667e7c28c04e807ba08f8c9e3bba32f14"}, + {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ca06675212f94e7a610e85ca36948bb8fc023e458dd6c63ef71abfd482481aa5"}, + {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5aef935237d60a51a62b86249839b51345f47564208c6ee615ed2a40878dccdd"}, + {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2b134fd795e2322b7684155b7855cc99409d10b2e408056db2b93b51a52accc7"}, + {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d25039a474c4c72a5ad4b52495056f843a7ff07b632c1b92ea9043a3d9950f6e"}, + {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f7d6b36dd2e029b6bcb8a13cf19664c7b8e19ab3a58e0fefbb5b8461447ed5ec"}, + {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:957b4774373cf6f709359e5c8c4a0af9f6d7875db657adb0feaf8d6cb3c3964c"}, + {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:d7eeb6d22331e2fd42fce928a81c697c9ee2d51400bd1a28803965883e13cead"}, + {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:6a962e04b8f91f8c4e5917e518d17958e3bdee71fd1d8b88cdce74dd0ebbf434"}, + {file = "yarl-1.9.4-cp37-cp37m-win32.whl", hash = "sha256:f3bc6af6e2b8f92eced34ef6a96ffb248e863af20ef4fde9448cc8c9b858b749"}, + {file = "yarl-1.9.4-cp37-cp37m-win_amd64.whl", hash = "sha256:ad4d7a90a92e528aadf4965d685c17dacff3df282db1121136c382dc0b6014d2"}, + {file = "yarl-1.9.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ec61d826d80fc293ed46c9dd26995921e3a82146feacd952ef0757236fc137be"}, + {file = "yarl-1.9.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8be9e837ea9113676e5754b43b940b50cce76d9ed7d2461df1af39a8ee674d9f"}, + {file = "yarl-1.9.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:bef596fdaa8f26e3d66af846bbe77057237cb6e8efff8cd7cc8dff9a62278bbf"}, + {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d47552b6e52c3319fede1b60b3de120fe83bde9b7bddad11a69fb0af7db32f1"}, + {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84fc30f71689d7fc9168b92788abc977dc8cefa806909565fc2951d02f6b7d57"}, + {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4aa9741085f635934f3a2583e16fcf62ba835719a8b2b28fb2917bb0537c1dfa"}, + {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:206a55215e6d05dbc6c98ce598a59e6fbd0c493e2de4ea6cc2f4934d5a18d130"}, + {file = "yarl-1.9.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07574b007ee20e5c375a8fe4a0789fad26db905f9813be0f9fef5a68080de559"}, + {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5a2e2433eb9344a163aced6a5f6c9222c0786e5a9e9cac2c89f0b28433f56e23"}, + {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6ad6d10ed9b67a382b45f29ea028f92d25bc0bc1daf6c5b801b90b5aa70fb9ec"}, + {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:6fe79f998a4052d79e1c30eeb7d6c1c1056ad33300f682465e1b4e9b5a188b78"}, + {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a825ec844298c791fd28ed14ed1bffc56a98d15b8c58a20e0e08c1f5f2bea1be"}, + {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8619d6915b3b0b34420cf9b2bb6d81ef59d984cb0fde7544e9ece32b4b3043c3"}, + {file = "yarl-1.9.4-cp38-cp38-win32.whl", hash = "sha256:686a0c2f85f83463272ddffd4deb5e591c98aac1897d65e92319f729c320eece"}, + {file = "yarl-1.9.4-cp38-cp38-win_amd64.whl", hash = "sha256:a00862fb23195b6b8322f7d781b0dc1d82cb3bcac346d1e38689370cc1cc398b"}, + {file = "yarl-1.9.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:604f31d97fa493083ea21bd9b92c419012531c4e17ea6da0f65cacdcf5d0bd27"}, + {file = "yarl-1.9.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8a854227cf581330ffa2c4824d96e52ee621dd571078a252c25e3a3b3d94a1b1"}, + {file = "yarl-1.9.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ba6f52cbc7809cd8d74604cce9c14868306ae4aa0282016b641c661f981a6e91"}, + {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a6327976c7c2f4ee6816eff196e25385ccc02cb81427952414a64811037bbc8b"}, + {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8397a3817d7dcdd14bb266283cd1d6fc7264a48c186b986f32e86d86d35fbac5"}, + {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e0381b4ce23ff92f8170080c97678040fc5b08da85e9e292292aba67fdac6c34"}, + {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23d32a2594cb5d565d358a92e151315d1b2268bc10f4610d098f96b147370136"}, + {file = "yarl-1.9.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ddb2a5c08a4eaaba605340fdee8fc08e406c56617566d9643ad8bf6852778fc7"}, + {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:26a1dc6285e03f3cc9e839a2da83bcbf31dcb0d004c72d0730e755b33466c30e"}, + {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:18580f672e44ce1238b82f7fb87d727c4a131f3a9d33a5e0e82b793362bf18b4"}, + {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:29e0f83f37610f173eb7e7b5562dd71467993495e568e708d99e9d1944f561ec"}, + {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:1f23e4fe1e8794f74b6027d7cf19dc25f8b63af1483d91d595d4a07eca1fb26c"}, + {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:db8e58b9d79200c76956cefd14d5c90af54416ff5353c5bfd7cbe58818e26ef0"}, + {file = "yarl-1.9.4-cp39-cp39-win32.whl", hash = "sha256:c7224cab95645c7ab53791022ae77a4509472613e839dab722a72abe5a684575"}, + {file = "yarl-1.9.4-cp39-cp39-win_amd64.whl", hash = "sha256:824d6c50492add5da9374875ce72db7a0733b29c2394890aef23d533106e2b15"}, + {file = "yarl-1.9.4-py3-none-any.whl", hash = "sha256:928cecb0ef9d5a7946eb6ff58417ad2fe9375762382f1bf5c55e61645f2c43ad"}, + {file = "yarl-1.9.4.tar.gz", hash = "sha256:566db86717cf8080b99b58b083b773a908ae40f06681e87e589a976faf8246bf"}, +] + +[package.dependencies] +idna = ">=2.0" +multidict = ">=4.0" + +[[package]] +name = "zipp" +version = "3.19.2" +description = "Backport of pathlib-compatible object wrapper for zip files" +optional = false +python-versions = ">=3.8" +files = [ + {file = "zipp-3.19.2-py3-none-any.whl", hash = "sha256:f091755f667055f2d02b32c53771a7a6c8b47e1fdbc4b72a8b9072b3eef8015c"}, + {file = "zipp-3.19.2.tar.gz", hash = "sha256:bf1dcf6450f873a13e952a29504887c89e6de7506209e5b1bcc3460135d4de19"}, +] + +[package.extras] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"] + +[metadata] +lock-version = "2.0" +python-versions = ">=3.9, <3.13" +content-hash = "2fdac68071dd0fe9f0c7481a6d7b61a27ae8b26b5e9dd1022f2fd80df5d0dcb3" diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..54e35da --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,157 @@ +[tool.poetry] +name = "mmlearn" +version = "0.1.0a0.dev0" # https://www.python.org/dev/peps/pep-0440/#version-schemes +description = "A modular framework for research on multimodal representation learning." +readme = "README.md" +authors = ["Vector AI Engineering "] +license = "Apache-2.0" +repository = "https://github.com/VectorInstitute/mmlearn" +packages = [ + { include = "mmlearn"}, +] + +[tool.poetry.dependencies] +python = ">=3.9, <3.13" +numpy = "^2.0.0" +hydra-core = "^1.3.0" +hydra-zen = "^0.13.0" +hydra-submitit-launcher = "^1.2.0" +transformers = "^4.44.0" +torch = {version = "^2.3.0", source = "torch-cu121"} +lightning = "^2.3.0" +jsonlines = "^4.0.0" +pandas = "^2.2.2" + +[tool.poetry.group.vision.dependencies] +torchvision = {version = "^0.18.0", source = "torch-cu121"} +opencv-python = "^4.10.0.84" +timm = "^1.0.7" + +[tool.poetry.group.video.dependencies] +pytorchvideo = {git = "https://github.com/facebookresearch/pytorchvideo", branch = "main"} +decord = "^0.6.0" + +[tool.poetry.group.audio.dependencies] +torchaudio = {version = "^2.3.0", source = "torch-cu121"} + +[tool.poetry.group.peft] +optional = true + +[tool.poetry.group.peft.dependencies] +peft = "^0.12.0" + +[tool.poetry.group.dev] +optional = true + +[tool.poetry.group.dev.dependencies] +wandb = "^0.17.6" +ipykernel = "^6.29.5" +ipython = "8.18.0" + +[tool.poetry.group.test] +optional = true + +[tool.poetry.group.test.dependencies] +ruff = "^0.5.6" +mypy = "^1.11.1" +pytest = "^8.3.2" +pre-commit = "^3.8.0" +nbqa = { version = "^1.8.7", extras = ["toolchain"] } +pip-audit = "^2.7.3" +typos = "^1.23.6" + +[[tool.poetry.source]] +name = "torch-cu121" +url = "https://download.pytorch.org/whl/cu121" +priority = "supplemental" + +[tool.mypy] +ignore_missing_imports = true +install_types = true +pretty = true +namespace_packages = true +explicit_package_bases = true +non_interactive = true +warn_unused_configs = true +allow_any_generics = false +allow_untyped_calls = false +allow_untyped_defs = false +allow_incomplete_defs = false +check_untyped_defs = true +allow_untyped_decorators = false +warn_unreachable = true +warn_redundant_casts = true +warn_unused_ignores = true +warn_return_any = true +implicit_reexport = false +strict_equality = true +extra_checks = true + +[tool.ruff] +include = ["*.py", "pyproject.toml", "*.ipynb"] +line-length = 88 + +[tool.ruff.format] +quote-style = "double" +indent-style = "space" +docstring-code-format = true + +[tool.ruff.lint] +select = [ + "A", # flake8-builtins + "B", # flake8-bugbear + "COM", # flake8-commas + "C4", # flake8-comprehensions + "RET", # flake8-return + "SIM", # flake8-simplify + "ICN", # flake8-import-conventions + "Q", # flake8-quotes + "RSE", # flake8-raise + "D", # pydocstyle + "E", # pycodestyle + "F", # pyflakes + "I", # isort + "W", # pycodestyle + "N", # pep8-naming + "ERA", # eradicate + "PL", # pylint +] +fixable = ["A", "B", "COM", "C4", "RET", "SIM", "ICN", "Q", "RSE", "D", "E", "F", "I", "W", "N", "ERA", "PL"] +ignore = [ + "E501", # line length violation + "C901", # `function_name` is too complex + "PLR0913", # Too many arguments + "PLR2004", # Magic value used in comparison + "COM812", # Missing trailing comma +] + +# Ignore import violations in all `__init__.py` files. +[tool.ruff.lint.per-file-ignores] +"__init__.py" = ["E402", "F401", "F403", "F811"] + +[tool.ruff.lint.pep8-naming] +ignore-names = ["X*", "setUp*"] + +[tool.ruff.lint.isort] +lines-after-imports = 2 + +[tool.ruff.lint.pycodestyle] +max-doc-length = 88 + +[tool.ruff.lint.pydocstyle] +convention = "numpy" + +[tool.pytest.ini_options] +pythonpath = [ + "." +] +markers = [ + "integration_test: marks tests as integration tests", +] + +[tool.poetry.scripts] +mmlearn_run = 'mmlearn.cli.run:main' + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..d420712 --- /dev/null +++ b/tests/__init__.py @@ -0,0 +1 @@ +"""Tests.""" diff --git a/tests/datasets/__init__.py b/tests/datasets/__init__.py new file mode 100644 index 0000000..bb10e9b --- /dev/null +++ b/tests/datasets/__init__.py @@ -0,0 +1 @@ +"""Tests for data loaders and processors.""" diff --git a/tests/datasets/test_combined_dataset.py b/tests/datasets/test_combined_dataset.py new file mode 100644 index 0000000..b0ea5a7 --- /dev/null +++ b/tests/datasets/test_combined_dataset.py @@ -0,0 +1,175 @@ +"""Tests for the combined dataset object and sampler.""" + +from collections.abc import Iterator + +import pytest +import torch +from torch.utils.data import DataLoader +from torch.utils.data.dataset import Dataset, IterableDataset + +from mmlearn.datasets.core.combined_dataset import CombinedDataset +from mmlearn.datasets.core.example import Example, collate_example_list +from mmlearn.datasets.core.samplers import CombinedDatasetRatioSampler + + +class DummyIterableDataset(IterableDataset): + """Dummy iterable dataset.""" + + def __init__(self) -> None: + """Initialize the dataset.""" + super().__init__() + self.examples = [10, 20, 30, 40, 50, 60, 70] + + def __iter__(self) -> Iterator[Example]: + """Yield dummy examples.""" + for example in self.examples: + yield Example({"tens": example}) + + def __len__(self) -> int: + """Return the length of the dataset.""" + return len(self.examples) + + +class DummyNegativesDataset(Dataset): + """Dummy dataset with negative examples.""" + + def __init__(self) -> None: + super().__init__() + self.tensors = torch.tensor([-1, -2, -3, -4, -5, -6, -7, -8, -9, -10]) + + def __getitem__(self, index: int) -> Example: + """Return an example from the dataset.""" + return Example({"negs": self.tensors[index]}) + + def __len__(self) -> int: + """Return the length of the dataset.""" + return len(self.tensors) + + +def test_combined_dataset(): + """Test the combined dataset object.""" + dataset1 = DummyNegativesDataset() + dataset2 = DummyIterableDataset() + combined_dataset = CombinedDataset([dataset1, dataset2]) + assert len(combined_dataset.datasets) == 2 + + with pytest.warns( + UserWarning, + match="Cannot create `example_ids` without `example_index` and `dataset_index`.*", + ): + example = combined_dataset[0] + assert isinstance(example, Example) + assert hasattr(example, "negs") + assert torch.equal(example.negs, torch.tensor(-1)) + assert example.dataset_index == 0 + + with pytest.warns( + UserWarning, + match="Cannot create `example_ids` without `example_index` and `dataset_index`.*", + ): + example = combined_dataset[-1] + assert isinstance(example, Example) + assert hasattr(example, "tens") + assert example.tens == 10 + assert example.dataset_index == 1 + + non_dataset = "not a dataset" + with pytest.raises(TypeError): + combined_dataset = CombinedDataset([dataset1, dataset2, non_dataset]) + + with pytest.raises(ValueError): + combined_dataset = CombinedDataset([]) + + +@pytest.mark.integration_test() +class TestCombinedDatasetRatioSampler: + """Test the combined dataset ratio sampler.""" + + def test_combined_dataset_ratio_sampler(self): + """Test the combined dataset ratio sampler.""" + dataset1 = DummyNegativesDataset() + dataset2 = DummyIterableDataset() + combined_dataset = CombinedDataset([dataset1, dataset2]) + sampler = CombinedDatasetRatioSampler( + combined_dataset, [0.5, 0.5], num_samples=10, rank=0, num_replicas=1 + ) + + assert len(sampler) == 10 + iterator = iter(sampler) + + # get 10 samples + sample_indices = [next(iterator) for _ in range(10)] + + # half the indices should be for the negatives and half should be positives + num_negs = sum(1 for index in sample_indices if index < 10) + num_pos = sum(1 for index in sample_indices if index >= 10) + assert num_negs == 5 + assert num_pos == 5 + + def test_combined_dataset_ratio_sampler_single_batch(self): + """Test the combined dataset ratio sampler with a single batch.""" + dataset1 = DummyNegativesDataset() + dataset2 = DummyIterableDataset() + combined_dataset = CombinedDataset([dataset1, dataset2]) + # oversample the iterable dataset to test StopIteration handling + sampler = CombinedDatasetRatioSampler( + combined_dataset, + [0.5, 0.5], + shuffle=False, # dataset samples will appear in order + num_samples=14, # 7 from each dataset + rank=0, + num_replicas=1, + ) + dataloader = DataLoader( + combined_dataset, + batch_size=1, + sampler=sampler, + collate_fn=collate_example_list, + ) + + with pytest.warns( + UserWarning, + match="Cannot create `example_ids` without `example_index` and `dataset_index`.*", + ): + for i, batch in enumerate(dataloader): + if i < 7: + assert "negs" in batch + assert torch.all(batch["negs"] < 0) + else: + assert "tens" in batch + assert torch.all(batch["tens"] % 10 == 0) + + def test_combined_dataset_ratio_sampler_batched_multiple_epochs(self): + """Test the combined dataset ratio sampler with multiple epochs.""" + dataset1 = DummyNegativesDataset() + dataset2 = DummyIterableDataset() + combined_dataset = CombinedDataset([dataset1, dataset2]) + # oversample both datasets + sampler = CombinedDatasetRatioSampler( + combined_dataset, + [0.5, 0.5], + shuffle=False, + num_samples=34, # 17 from each dataset + rank=0, + num_replicas=1, + replacement=True, + ) + dataloader = DataLoader( + combined_dataset, + batch_size=2, + sampler=sampler, + collate_fn=collate_example_list, + ) + + with pytest.warns( + UserWarning, + match="Cannot create `example_ids` without `example_index` and `dataset_index`.*", + ): + for _ in range(2): + for i, batch in enumerate(dataloader): + if i < 9: + assert "negs" in batch + assert torch.all(batch["negs"] < 0) + else: + assert "tens" in batch + assert torch.all(batch["tens"] % 10 == 0) diff --git a/tests/datasets/test_example.py b/tests/datasets/test_example.py new file mode 100644 index 0000000..4890581 --- /dev/null +++ b/tests/datasets/test_example.py @@ -0,0 +1,195 @@ +"""Tests for `dataset.example` module.""" + +from collections import namedtuple + +import numpy as np +import pytest +import torch + +from mmlearn.datasets.core.example import ( + Example, + collate_example_list, + find_matching_indices, +) + + +def test_example(): + """Test `dataset.example.Example`.""" + # happy path + an_example = Example() + assert len(an_example) == 0 + + an_example.text = "Hello" + assert len(an_example) == 1 + assert an_example["text"] == "Hello" + assert an_example.text == "Hello" + + init_dict = { + "text": "Hello", + "number": 123, + "list": [1, 2, 3], + "tensor": torch.tensor(1), + "point": namedtuple("Point", ["x", "y"])(1, 2), + "mapping": {"a": 1, "b": 2}, + "nested_mapping": {"a": {"b": 1}}, + } + init_example = Example(init_dict=init_dict) + assert len(init_example) == 7 + assert init_dict == dict(init_example) + + # test example_id + init_example.dataset_index = 1 + init_example.example_index = 2 + init_example.create_ids() + + assert all( + key in init_example.example_ids + and torch.equal(init_example.example_ids[key], torch.tensor([1, 2])) + for key in init_dict + ) + + # error path + with pytest.raises(TypeError, match="'int' object is not iterable.*"): + example = Example(123) + + example = Example({"text": torch.tensor(2)}) + with pytest.raises(AttributeError): + example.non_existent_attribute # noqa: B018 + + +def test_collate_example_list(): + """Test `dataset.example.collate_example_list`.""" + img_class_example = Example( + {"image": torch.tensor(1), "class_label": torch.tensor(2)}, + ) + img_text_pair = Example({"image": torch.tensor(3), "text": "Hello"}) + audio_text_pair = Example({"audio": torch.tensor(4), "text": "World"}) + nested_example = Example( + { + "an_int": 1, + "a_float": 1.0, + "a_list": [1, 2, 3], + "a_tuple": (1, 2, 3), + "a_tensor": torch.tensor(1), + "a_mapping": {"a": 1, "b": 2}, + "a_nested_mapping": {"a": {"b": 1}}, + "a_double_nested_mapping": {"a": {"b": {"c": 1}}}, + "a_namedtuple": namedtuple("Point", ["x", "y"])(1, 2), + "a_numpy_array": np.array([1, 2, 3]), + }, + ) + expected_result = { + "image": torch.tensor([1, 3]), + "class_label": torch.tensor([2]), + "text": ["Hello", "World"], + "audio": torch.tensor([4]), + "an_int": torch.tensor([1]), + "a_float": torch.tensor([1.0], dtype=torch.float64), + "a_list": [torch.tensor([1]), torch.tensor([2]), torch.tensor([3])], + "a_tuple": [torch.tensor([1]), torch.tensor([2]), torch.tensor([3])], + "a_tensor": torch.tensor([1]), + "a_mapping": {"a": torch.tensor([1]), "b": torch.tensor([2])}, + "a_nested_mapping": {"a": {"b": torch.tensor([1])}}, + "a_double_nested_mapping": {"a": {"b": {"c": torch.tensor([1])}}}, + "a_namedtuple": namedtuple("Point", ["x", "y"])( + torch.tensor([1]), + torch.tensor([2]), + ), + "a_numpy_array": torch.tensor([[1, 2, 3]]), + } + result = collate_example_list( + [img_class_example, img_text_pair, audio_text_pair, nested_example], + ) + for key in expected_result: + assert key in result + if isinstance(expected_result[key], torch.Tensor): + assert torch.equal(result[key], expected_result[key]) + elif isinstance(expected_result[key], list) and isinstance( + expected_result[key][0], + torch.Tensor, + ): + assert all( + torch.equal(result[key][i], expected_result[key][i]) + for i in range(len(expected_result[key])) + ) + elif isinstance(expected_result[key], dict): + for sub_key in expected_result[key]: + assert sub_key in result[key] + if isinstance(expected_result[key][sub_key], torch.Tensor): + assert torch.equal( + result[key][sub_key], + expected_result[key][sub_key], + ) + elif isinstance(expected_result[key][sub_key], dict): + for sub_sub_key in expected_result[key][sub_key]: + if isinstance( + expected_result[key][sub_key][sub_sub_key], + torch.Tensor, + ): + assert torch.equal( + result[key][sub_key][sub_sub_key], + expected_result[key][sub_key][sub_sub_key], + ) + elif isinstance( + expected_result[key][sub_key][sub_sub_key], + dict, + ): + for sub_sub_sub_key in expected_result[key][sub_key][ + sub_sub_key + ]: + assert torch.equal( + result[key][sub_key][sub_sub_key][sub_sub_sub_key], + expected_result[key][sub_key][sub_sub_key][ + sub_sub_sub_key + ], + ) + else: + assert result[key] == expected_result[key] + + +def test_find_matching_indices(): + """Test `dataset.example.find_matching_indices`.""" + first_example_ids = torch.tensor([(0, 0), (0, 1), (1, 0), (1, 1)]) + second_example_ids = torch.tensor([(1, 0), (1, 1), (2, 0), (2, 1), (2, 2)]) + expected_result = (torch.tensor([2, 3]), torch.tensor([0, 1])) + result = find_matching_indices(first_example_ids, second_example_ids) + assert isinstance(result, tuple) + assert isinstance(result[0], torch.Tensor) + assert isinstance(result[1], torch.Tensor) + assert torch.equal(result[0], expected_result[0]) + assert torch.equal(result[1], expected_result[1]) + + # duplicates + first_example_ids = torch.tensor([(0, 0), (0, 1), (1, 0), (1, 1), (1, 1)]) + second_example_ids = torch.tensor([(1, 0), (1, 1), (2, 0), (2, 1), (2, 2)]) + expected_result = (torch.tensor([2, 3, 4]), torch.tensor([0, 1, 1])) + result = find_matching_indices(first_example_ids, second_example_ids) + assert isinstance(result, tuple) + assert isinstance(result[0], torch.Tensor) + assert isinstance(result[1], torch.Tensor) + assert torch.equal(result[0], expected_result[0]) + assert torch.equal(result[1], expected_result[1]) + + # no matches + first_example_ids = torch.tensor([(0, 0), (0, 1), (1, 0), (1, 1)]) + second_example_ids = torch.tensor([(2, 0), (2, 1), (2, 2)]) + expected_result = (torch.tensor([]), torch.tensor([])) + result = find_matching_indices(first_example_ids, second_example_ids) + assert isinstance(result, tuple) + assert isinstance(result[0], torch.Tensor) + assert isinstance(result[1], torch.Tensor) + assert torch.equal(result[0], expected_result[0]) + assert torch.equal(result[1], expected_result[1]) + + first_example_ids = [(0, 0), (0, 1), (1, 0), (1, 1)] + second_example_ids = torch.tensor([(1, 0), (1, 1), (2, 0), (2, 1), (2, 2)]) + with pytest.raises(TypeError, match="Expected inputs to be tensors, but got.*"): + find_matching_indices(first_example_ids, second_example_ids) + + first_example_ids = torch.tensor([(0,), (0,), (1,), (1,)]) + second_example_ids = torch.tensor([(1, 0), (1, 1), (2, 0), (2, 1), (2, 2)]) + with pytest.raises( + ValueError, + match=r"Expected argument `first_example_ids` to be a tensor of shape \(N, 2\).*", + ): + find_matching_indices(first_example_ids, second_example_ids) diff --git a/tests/datasets/test_masking.py b/tests/datasets/test_masking.py new file mode 100644 index 0000000..fee8780 --- /dev/null +++ b/tests/datasets/test_masking.py @@ -0,0 +1,35 @@ +"""Test functionality of different transform module.""" + +import numpy as np +import torch + +from mmlearn.datasets.core import Modalities +from mmlearn.datasets.processors.masking import RandomMaskGenerator +from mmlearn.datasets.processors.tokenizers import HFTokenizer + + +torch.manual_seed(42) +np.random.seed(42) + + +def test_random_masking() -> None: + """Test random mask generator.""" + mask_probability = 0.5 + checkpoint_name = "roberta-base" + mask_generator = RandomMaskGenerator(mask_probability) + tokenizer = HFTokenizer(checkpoint_name, max_length=512, padding="max_length") + tokenizer_output = tokenizer( + "A photo of a cat, a quick brown fox jumps over the lazy dog!" + ) + + torch.testing.assert_close( + tokenizer_output[Modalities.TEXT][:4], torch.tensor([0, 250, 1345, 9]) + ) + + _, _, mask = mask_generator( + tokenizer_output, + tokenizer.tokenizer, # type: ignore + ) + + assert len(mask) == 512 + assert sum(mask) != 0, "Mask should not be empty" diff --git a/tests/modules/__init__.py b/tests/modules/__init__.py new file mode 100644 index 0000000..bcf44da --- /dev/null +++ b/tests/modules/__init__.py @@ -0,0 +1 @@ +"""Tests for modules.""" diff --git a/tests/modules/test_ema.py b/tests/modules/test_ema.py new file mode 100644 index 0000000..401b8bd --- /dev/null +++ b/tests/modules/test_ema.py @@ -0,0 +1,58 @@ +"""Test functionality of the ExponentialMovingAverage .""" + +import pytest +import torch +import torchvision + +from mmlearn.modules.ema import ExponentialMovingAverage + + +torch.manual_seed(42) + + +@pytest.mark.integration_test() +def test_ema() -> None: + """Test ExponentialMovingAverage.""" + model = torchvision.models.resnet18() + ema = ExponentialMovingAverage( + model=model, + ema_decay=0.9998, + ema_end_decay=0.9999, + ema_anneal_end_step=300000, + ) + ema.model = ema.model.cpu() # for testing purposes + + # test output between model and ema model + model_input = torch.rand(1, 3, 224, 224) + output = model(model_input) + output_ema = ema.model(model_input) + torch.testing.assert_close(output, output_ema) + + # change the model parameters to simulate training + model_updated = torchvision.models.resnet18() + + # test ema step + ema.step(model_updated) + + # Validate that the EMA step has updated the model parameters + ema_state_dict = ema.model.state_dict() + model_updated_state_dict = model_updated.state_dict() + for param_name, param in model.state_dict().items(): + ema_param = ema_state_dict[param_name].float() + model_updated_param = model_updated_state_dict[param_name].float() + + if param_name in ema.skip_keys or not param.requires_grad: + assert torch.allclose( + ema_param, + model_updated_param, + ), f"Unexpected EMA parameter value for {param_name}" + elif len(param.shape) > 0: # check only if it is model parameter + expected_ema_param = param.mul(ema.decay).add( + model_updated_param.to(dtype=ema_param.dtype), + alpha=1 - ema.decay, + ) + + assert torch.allclose( + ema_param, + expected_ema_param, + ), f"Unexpected EMA parameter value for {param_name}"