From ed286401626fba888f260aba7676da1bdb830762 Mon Sep 17 00:00:00 2001 From: GitHub CI Date: Thu, 17 Jul 2025 14:46:06 +0200 Subject: [PATCH 1/9] feat: enhance Docker build script and add initial Dockerfile for STAMP integration Signed-off-by: GitHub CI --- application/jobs/stamp/README.md | 97 ++++++ application/jobs/stamp/getting-started.md | 398 ++++++++++++++++++++++ application/jobs/stamp/meta.conf | 9 + buildDockerImageAndStartupKits.sh | 84 +++-- docker_config/Dockerfile_stamp | 58 ++++ 5 files changed, 612 insertions(+), 34 deletions(-) create mode 100644 application/jobs/stamp/README.md create mode 100644 application/jobs/stamp/getting-started.md create mode 100644 application/jobs/stamp/meta.conf create mode 100644 docker_config/Dockerfile_stamp diff --git a/application/jobs/stamp/README.md b/application/jobs/stamp/README.md new file mode 100644 index 00000000..7de86729 --- /dev/null +++ b/application/jobs/stamp/README.md @@ -0,0 +1,97 @@ +# STAMP: A Protocol for Solid Tumor Associative Modeling in Pathology + + + +![CI](https://github.com/KatherLab/STAMP/actions/workflows/ci.yml/badge.svg) + +This repository contains the accompanying code for the steps described in the [Nature Protocols paper][stamp paper]: +"From Whole Slide Image to Biomarker Prediction: +A Protocol for End-to-End Deep Learning in Computational Pathology". + +> [!NOTE] +> This repo contains an updated version of the codebase. +> For a version compatible with the instructions in the paper, +> please check out [version 1 of STAMP][stamp v1]. + +[stamp paper]: https://www.nature.com/articles/s41596-024-01047-2 "From whole-slide image to biomarker prediction: end-to-end weakly supervised deep learning in computational pathology" + +[stamp v1]: https://github.com/KatherLab/STAMP/tree/v1 + +## Installing stamp + +We recommend installing STAMP with [uv](https://docs.astral.sh/uv/): + +```bash +git clone https://github.com/KatherLab/STAMP.git + +cd STAMP/ + +uv sync --all-extras + +source .venv/bin/activate +``` + +> [!IMPORTANT] +> STAMP additionally requires OpenSlide to be installed, as well as OpenCV dependencies. +> +> For Ubuntu < 23.10: +> ```bash +> apt update && apt install -y openslide-tools libgl1-mesa-glx # libgl1-mesa-glx is needed for OpenCV +> ``` +> +> For Ubuntu >= 23.10: +> ```bash +> apt update && apt install -y openslide-tools libgl1 libglx-mesa0 libglib2.0-0 # libgl1, libglx-mesa0, libglib2.0-0 are needed for OpenCV +> ``` + +If the installation was successful, running `stamp` in your terminal should yield the following output: + +``` +$ stamp +usage: stamp [-h] [--config CONFIG_FILE_PATH] {init,preprocess,encode_slides,encode_patients,train,crossval,deploy,statistics,config,heatmaps} ... + +STAMP: Solid Tumor Associative Modeling in Pathology + +positional arguments: + {init,preprocess,encode_slides,encode_patients,train,crossval,deploy,statistics,config,heatmaps} + init Create a new STAMP configuration file at the path specified by --config + preprocess Preprocess whole-slide images into feature vectors + encode_slides Encode patch-level features into slide-level embeddings + encode_patients Encode features into patient-level embeddings + train Train a Vision Transformer model + crossval Train a Vision Transformer model with cross validation for modeling.n_splits folds + deploy Deploy a trained Vision Transformer model + statistics Generate AUROCs and AUPRCs with 95%CI for a trained Vision Transformer model + config Print the loaded configuration + heatmaps Generate heatmaps for a trained model + +options: + -h, --help show this help message and exit + --config CONFIG_FILE_PATH, -c CONFIG_FILE_PATH + Path to config file. Default: config.yaml +``` + +## Running stamp + +For a quick introduction how to run stamp, +check out our [getting started guide](getting-started.md). + +## Reference + +If you find our work useful in your research +or if you use parts of this code +please consider citing our [Nature Protocols publication](https://www.nature.com/articles/s41596-024-01047-2): + +``` +@Article{ElNahhas2024, + author={El Nahhas, Omar S. M. and van Treeck, Marko and W{\"o}lflein, Georg and Unger, Michaela and Ligero, Marta and Lenz, Tim and Wagner, Sophia J. and Hewitt, Katherine J. and Khader, Firas and Foersch, Sebastian and Truhn, Daniel and Kather, Jakob Nikolas}, + title={From whole-slide image to biomarker prediction: end-to-end weakly supervised deep learning in computational pathology}, + journal={Nature Protocols}, + year={2024}, + month={Sep}, + day={16}, + issn={1750-2799}, + doi={10.1038/s41596-024-01047-2}, + url={https://doi.org/10.1038/s41596-024-01047-2} +} +``` diff --git a/application/jobs/stamp/getting-started.md b/application/jobs/stamp/getting-started.md new file mode 100644 index 00000000..87010516 --- /dev/null +++ b/application/jobs/stamp/getting-started.md @@ -0,0 +1,398 @@ +# Getting Started with Stamp + +This guide is designed to help you with your first steps using the stamp pipeline +to predict biomarkers and other attributes from whole slide images (WSIs). +To follow along, +you will need some WSIs, +a table mapping each of these slides to a patient +as well as some ground truth we will eventually train a neural network on. + +## Whole Slide Images + +The whole slide images have to be in any of the formats [supported by OpenSlide][openslide]. +For the next steps we assume that all these WSIs are stored in the same directory. +We will call this directory the _WSI directory_. + +[openslide]: https://openslide.org/#about-openslide "About OpenSlide" + +## Creating a Configuration File + +Stamp is configured using configuration files. +We recommend creating one configuration file per experiment +and storing in the same folder as the eventual results, +as this makes it easier to reconstruct which data and parameters a model was trained with later. + +The `stamp init` command creates a new configuration file with dummy values. +By default, it is created in `$PWD/config.yaml`, +but we can use the `--config` option to specify its location: + +```sh +# Create a directory to save our experiment results to +mkdir stamp-test-experiment +# Create a new config file in said directory +stamp --config stamp-test-experiment/config.yaml init +``` + +## Feature Extraction + +To do any kind of training on our data, we first have to convert it into a form +more easily usable by neural networks. +We do this using a _feature extractor_. +A feature extractor is a neural network has been trained on a large amount of WSIs +to extract extract the information relevant for our domain from images. +This way, we can compress WSIs into a more compact representation, +which in turn allows us to efficiently train machine learning models with them. + +Stamp currently supports the following feature extractors: + +- [ctranspath][ctranspath] +- [chief_ctranspath][chief_ctranspath] +- [DinoBloom][dinobloom] +- [CONCH][conch] +- [CONCHv1.5][conch1_5] +- [UNI][uni] +- [UNI2][uni2] +- [Virchow][virchow] +- [Virchow2][virchow2] +- [Gigapath][gigapath] +- [H-optimus-0][h_optimus_0] +- [H-optimus-1][h_optimus_1] +- [mSTAR][mstar] +- [MUSK][musk] +- [PLIP][plip] + +As some of the above require you to request access to the model on huggingface, +we will stick with ctranspath for this example. + +In order to use a feature extractor, +you also have to install their respective dependencies. +You can do so by specifying the feature extractor you want to use +when installing stamp: + +```sh +# Install stamp including the dependencies for all feature extractors +pip install "git+https://github.com/KatherLab/stamp@v2[all]" +``` + +Open the `stamp-test-experiment/config.yaml` we created in the last step +and modify the `output_dir`, `wsi_dir` and `cache_dir` entries +in the `preprocessing` section +to contain the absolute paths of the directory the configuration file resides in. +`wsi_dir` Needs to point to a path containing the WSIs you want to extract features from. + +The `cache_dir` will be used to save intermediate data. +Should you decide to try another feature extractor later, +using the same cache dir again will significantly speed up the extraction process. +If you will only extract features once, it can be set to `none`. + +```yaml +# stamp-test-experiment/config.yaml + +preprocessing: + output_dir: "/absolute/path/to/stamp-test-experiment" + wsi_dir: "/absolute/path/to/wsi_dir" + + # Other possible values are "mahmood-uni" and "mahmood-conch" + extractor: "ctranspath" + + # Having a cache dir will speed up extracting features multiple times, + # e.g. with different feature extractors. + # Optional. + cache_dir: "/absolute/path/to/stamp-test-experiment/../cache" + # If you do not want to use a cache, + # change the cache dir to the following: + # cache_dir: null + + # Device to run feature extraction on. + # Set this to "cpu" if you do not have a CUDA-capable GPU. + device: "cuda" + + # How many workers to use for tile extraction. Should be less or equal to + # the number of cores of your system. + max_workers: 8 +``` + +Extracting the features is then as easy as running + +```sh +stamp --config stamp-test-experiment/config.yaml preprocess +``` + +Depending on the size of your dataset and your hardware, +this process may take anything between a few hours and days. + +You can interrupt this process at any time. +It will continue where you stopped it the next time you run `stamp preprocess`. + +As the preprocessing is running, +you can see the output directory fill up with the features, saved in `.h5` files, +as well as `.jpg`s showing from which parts of the slide features are extracted. +Most of the background should be marked in red, +meaning ignored that it was ignored during feature extraction. + +> **If you are using the UNI or CONCH models** +> and working in an environment where your home directory storage is limited, +> you may want to also specify your huggingface storage directory +> by setting the `HF_HOME` environment variable: +> ```sh +> export HF_HOME=/path/to/directory/to/store/huggingface/data/in +> huggingface-cli login # only needs to be done once per $HF_HOME +> stamp -c stamp-test-experiment/config.yaml preprocess +> ``` + +[ctranspath]: https://www.sciencedirect.com/science/article/abs/pii/S1361841522002043 "Transformer-based unsupervised contrastive learning for histopathological image classification" + +[dinobloom]: https://github.com/marrlab/DinoBloom "DinoBloom: A Foundation Model for Generalizable Cell Embeddings in Hematology" + +[uni]: https://www.nature.com/articles/s41591-024-02857-3 "Towards a general-purpose foundation model for computational pathology" + +[uni2]: https://huggingface.co/MahmoodLab/UNI2-h + +[conch]: https://www.nature.com/articles/s41591-024-02856-4 "A visual-language foundation model for computational pathology" + +[conch1_5]: https://huggingface.co/MahmoodLab/conchv1_5 + +[virchow]: https://huggingface.co/paige-ai/Virchow "A foundation model for clinical-grade computational pathology and rare cancers detection" + +[virchow2]: https://huggingface.co/paige-ai/Virchow2 + +[chief_ctranspath]: https://github.com/hms-dbmi/CHIEF + +[gigapath]: https://huggingface.co/prov-gigapath/prov-gigapath + +[h_optimus_0]: https://huggingface.co/bioptimus/H-optimus-0 + +[h_optimus_1]: https://huggingface.co/bioptimus/H-optimus-1 + +[mstar]: https://huggingface.co/Wangyh/mSTAR + +[musk]: https://huggingface.co/xiangjx/musk + +[plip]: https://github.com/PathologyFoundation/plip + +[TITAN]: https://huggingface.co/MahmoodLab/TITAN + +[COBRA2]: https://huggingface.co/KatherLab/COBRA + +[EAGLE]: https://github.com/KatherLab/EAGLE + +[MADELEINE]: https://huggingface.co/MahmoodLab/madeleine + +## Doing Cross-Validation on the Data Set + +One way to quickly ascertain if a neural network can be trained to recognize a specific pattern +without the need to source a separate testing set +is to perform a cross-validation on it. +During a cross validation, +we train multiple models on a subset of the data, +testing its effectiveness on the held-out part of the data not used during training. +To perform a cross-validation, add the following lines to your `stamp-test-experiment/config.yaml`, +with `feature_dir` adapted to match the directory the `.h5` files were output to in the last step. +`clini_table` and `slide_table` both need to point to tables, +either in excel or `.csv` format, +with contents as described below. +Finally, `ground_truth_label` needs to contain the column name +of the data we want to train our model on. +Stamp only can be used to train neural networks for categorical targets. +We recommend explicitly setting the possible classes using the `categories` field. + +```yaml +# stamp-test-experiment/config.yaml + +crossval: + output_dir: "/absolute/path/to/stamp-test-experiment" + + # An excel (.xlsx) or CSV (.csv) table containing the clinical information of + # patients. Patients not present in this file will be ignored during training. + # Has to contain at least two columns, one titled "PATIENT", containing a patient ID, + # and a second column containing the categorical ground truths for that patient. + clini_table: "metadata-CRC/TCGA-CRC-DX_CLINI.xlsx" + + # Directory the extracted features are saved in. + feature_dir: "/absolute/path/to/stamp-test-experiment/xiyuewang-ctranspath-7c998680-112fc79c" + + # A table (.xlsx or .csv) relating every patient to their feature files. + # The table must contain at least two columns, one titled "PATIENT", + # containing the patient ID (matching those in the `clini_table`), and one + # called "FILENAME", containing the feature file path relative to `feature_dir`. + # Patient IDs not present in the clini table as well as non-existent feature + # paths are ignored. + slide_table: "slide.csv" + + # Name of the column from the clini table to train on. + ground_truth_label: "isMSIH" + + # Optional settings: + + # The categories occurring in the target label column of the clini table. + # If unspecified, they will be inferred from the table itself. + categories: [ "yes", "no" ] + + # Number of folds to split the data into for cross-validation + #n_splits: 5 +``` + +After specifying all the parameters of our cross-validation, +we can run it by invoking: + +```sh +stamp --config stamp-test-experiment/config.yaml crossval +``` + +## Generating Statistics + +After training and validating your model, you may want to generate statistics to evaluate its performance. +This can be done by adding a `statistics` section to your `stamp-test-experiment/config.yaml` file. +The configuration should look like this: + +```yaml +# stamp-test-experiment/config.yaml + +statistics: + output_dir: "/absolute/path/to/stamp-test-experiment/statistics" + + # Name of the target label. + ground_truth_label: "isMSIH" + + # A lot of the statistics are computed "one-vs-all", i.e. there needs to be + # a positive class to calculate the statistics for. + true_class: "yes" + + pred_csvs: + - "/absolute/path/to/stamp-test-experiment/split-0/patient-preds.csv" + - "/absolute/path/to/stamp-test-experiment/split-1/patient-preds.csv" + - "/absolute/path/to/stamp-test-experiment/split-2/patient-preds.csv" + - "/absolute/path/to/stamp-test-experiment/split-3/patient-preds.csv" + - "/absolute/path/to/stamp-test-experiment/split-4/patient-preds.csv" +``` + +To generate the statistics, run the following command: + +```sh +stamp --config stamp-test-experiment/config.yaml statistics +``` + +Afterwards, the `output_dir` should contain the following files: + +- `isMSIH-categorical-stats-individual.csv` contains statistical scores + for each individual split. +- `isMSIH-categorical-stats-aggregated.csv` contains the mean + as well as the 95% confidence interval for the statistical scores + for the splits. +- `roc-curve_isMSIH=yes.svg` and `pr-curve_isMSIH=yes.svg` + contain the ROC and precision recall curves of the splits. + +## Slide-Level Encoding + +Tile-Level features can be enconded into a single feature per slide, this is useful +when trying to capture global patterns across whole slides. + +STAMP currently supports the following encoders: + +- [CHIEF][CHIEF_CTRANSPATH] +- [TITAN] +- [GIGAPATH] +- [COBRA2] +- [EAGLE] +- [MADELEINE] + +Slide encoders take as input the already extracted tile-level features in the +preprocessing step. Each encoder accepts only certain extractors and most +work only on CUDA devices: + +| Encoder | Required Extractor | Compatible Devices | +|-----------|-------------------------------------|--------------------| +| CHIEF | CTRANSPATH, CHIEF-CTRANSPATH | CUDA only | +| TITAN | CONCH1.5 | CUDA, cpu, mps +| GIGAPATH | GIGAPATH | CUDA only +| COBRA2 | CONCH, UNI, VIRCHOW2 or H-OPTIMUS-0 | CUDA only +| EAGLE | CTRANSPATH, CHIEF-CTRANSPATH | CUDA only +| MADELEINE | CONCH | CUDA only + +As with feature extractors, most of these models require you to request +access. The following example uses CHIEF, which is available if you installed +STAMP with `uv sync --all-extras`. The configuration should look like this: + +```yaml +# stamp-test-experiment/config.yaml + +slide_encoding: + # Encoder to use for slide encoding. Possible options are "cobra", + # "eagle", "titan", "gigapath", "chief", "prism", "madeleine". + encoder: "chief" + + # Directory to save the output files. + output_dir: "/path/to/save/files/to" + + # Directory where the extracted features are stored. + feat_dir: "/path/your/extracted/features/are/stored/in" + + # Device to run slide encoding on ("cpu", "cuda", "cuda:0", etc.) + device: "cuda" + + # Optional settings: + # Directory where the aggregated features are stored. Needed for + # some encoders such as eagle (it requires virchow2 features). + #agg_feat_dir: "/path/your/aggregated/features/are/stored/in" + + # Add a hash of the entire preprocessing codebase in the feature folder name. + #generate_hash: True + ``` + +Don't forget to put in `feat_dir` a path containing, in this case, `ctranspath` or +`chief-ctranspath` tile-level features. Once everything is set, you can simply run: + +```sh +stamp --config stamp-test-experiment/config.yaml encode_slides +``` + +The output will be one `.h5` file per slide. + +## Patient-Level Encoding + +Even though the available encoders are designed for slide-level use, this +option concatenates the slides of a patient along the x-axis, creating a single +"virtual" slide that contains two blocks of tissue. The configuration is the same +except for `slide_table` which is required to link slides with patients. + +```yaml +# stamp-test-experiment/config.yaml + +patient_encoding: + # Encoder to use for patient encoding. Possible options are "cobra", + # "eagle", "titan", "gigapath", "chief", "prism", "madeleine". + encoder: "eagle" + + # Directory to save the output files. + output_dir: "/path/to/save/files/to" + + # Directory where the extracted features are stored. + feat_dir: "/path/your/extracted/features/are/stored/in" + + # A table (.xlsx or .csv) relating every slide to their feature files. + # The table must contain at least two columns, one titled "SLIDE", + # containing the slide ID, and one called "FILENAME", containing the feature file path relative to `feat_dir`. + slide_table: "/path/of/slide.csv" + + # Device to run slide encoding on ("cpu", "cuda", "cuda:0", etc.) + device: "cuda" + + # Optional settings: + patient_label: "PATIENT" + filename_label: "FILENAME" + + # Directory where the aggregated features are stored. Needed for + # some encoders such as eagle (it requires virchow2 features). + #agg_feat_dir: "/path/your/aggregated/features/are/stored/in" + + # Add a hash of the entire preprocessing codebase in the feature folder name. + #generate_hash: True + ``` + +Then run: + + ```sh +stamp --config stamp-test-experiment/config.yaml encode_patients +``` + +The output `.h5` features will have the patient's id as name. \ No newline at end of file diff --git a/application/jobs/stamp/meta.conf b/application/jobs/stamp/meta.conf new file mode 100644 index 00000000..13b8071b --- /dev/null +++ b/application/jobs/stamp/meta.conf @@ -0,0 +1,9 @@ +name = "stamp" +resource_spec {} +deploy_map { + app = [ + "@ALL" + ] +} +min_clients = 2 +mandatory_clients = [] diff --git a/buildDockerImageAndStartupKits.sh b/buildDockerImageAndStartupKits.sh index e1d582c8..b55d9692 100755 --- a/buildDockerImageAndStartupKits.sh +++ b/buildDockerImageAndStartupKits.sh @@ -8,72 +8,88 @@ if ! git diff --quiet || ! git diff --staged --quiet ; then exit 1 fi -DOCKER_BUILD_ARGS="--no-cache --progress=plain"; +DOCKER_BUILD_ARGS="--no-cache --progress=plain" +DOCKERFILE_SUFFIX="ODELIA" +PROJECT_FILE="" while [[ "$#" -gt 0 ]]; do case $1 in - -p) PROJECT_FILE="$2"; shift ;; - --use-docker-cache) DOCKER_BUILD_ARGS="";; + -p) PROJECT_FILE="$2"; shift ;; + --use-docker-cache) DOCKER_BUILD_ARGS="" ;; + -d) DOCKERFILE_SUFFIX="$2"; shift ;; *) echo "Unknown parameter passed: $1"; exit 1 ;; esac shift done if [ -z "$PROJECT_FILE" ]; then - echo "Usage: buildDockerImageAndStartupKits.sh -p [--use-docker-cache]" + echo "Usage: buildDockerImageAndStartupKits.sh -p [-d ] [--use-docker-cache]" exit 1 fi -VERSION=`./getVersionNumber.sh` -DOCKER_IMAGE=jefftud/odelia:$VERSION +VERSION=$(./getVersionNumber.sh) +DOCKER_IMAGE="jefftud/${DOCKERFILE_SUFFIX}:$VERSION" +DOCKERFILE_PATH="docker_config/Dockerfile_${DOCKERFILE_SUFFIX}" +if [[ ! -f $DOCKERFILE_PATH ]]; then + echo "Error: Dockerfile $DOCKERFILE_PATH does not exist." + exit 1 +fi # prepare clean version of source code repository clone for building Docker image -CWD=`pwd` -CLEAN_SOURCE_DIR=`mktemp -d` -mkdir $CLEAN_SOURCE_DIR/MediSwarm -cp -r . $CLEAN_SOURCE_DIR/MediSwarm/ -cd $CLEAN_SOURCE_DIR/MediSwarm +CWD=$(pwd) +CLEAN_SOURCE_DIR=$(mktemp -d) +mkdir -p "$CLEAN_SOURCE_DIR/MediSwarm" +cp -r . "$CLEAN_SOURCE_DIR/MediSwarm/" +cd "$CLEAN_SOURCE_DIR/MediSwarm" git clean -x -q -f . cd docker_config/NVFlare git clean -x -q -f . cd ../.. -rm .git -rf +rm -rf .git chmod a+rX . -R -cd $CWD - +cd "$CWD" # prepare pre-trained model weights for being included in Docker image -MODEL_WEIGHTS_FILE='docker_config/torch_home_cache/hub/checkpoints/dinov2_vits14_pretrain.pth' -MODEL_LICENSE_FILE='docker_config/torch_home_cache/hub/facebookresearch_dinov2_main/LICENSE' -if [[ ! -f $MODEL_WEIGHTS_FILE || ! -f $MODEL_LICENSE_FILE ]]; then - echo "Pre-trained model not available. Attempting download" - HUBDIR=$(dirname $(dirname $MODEL_LICENSE_FILE)) - mkdir -p $(dirname $MODEL_WEIGHTS_FILE) - wget https://dl.fbaipublicfiles.com/dinov2/dinov2_vits14/dinov2_vits14_pretrain.pth -O $MODEL_WEIGHTS_FILE - wget https://github.com/facebookresearch/dinov2/archive/refs/heads/main.zip -O /tmp/dinov2.zip - unzip /tmp/dinov2.zip -d $HUBDIR - mv $HUBDIR/dinov2-main $HUBDIR/$(basename $(dirname $MODEL_LICENSE_FILE)) - touch $HUBDIR/trusted_list -fi - -if echo 2e405cee1bad14912278296d4f42e993 $MODEL_WEIGHTS_FILE | md5sum --check - && echo 153d2db1c329326a2d9f881317ea942e $MODEL_LICENSE_FILE | md5sum --check -; then - cp -r ./docker_config/torch_home_cache $CLEAN_SOURCE_DIR/torch_home_cache +if [[ "$DOCKERFILE_SUFFIX" == "ODELIA" ]]; then + echo "Preparing DINOv2 model weights for ODELIA build..." + + MODEL_WEIGHTS_FILE='docker_config/torch_home_cache/hub/checkpoints/dinov2_vits14_pretrain.pth' + MODEL_LICENSE_FILE='docker_config/torch_home_cache/hub/facebookresearch_dinov2_main/LICENSE' + + if [[ ! -f $MODEL_WEIGHTS_FILE || ! -f $MODEL_LICENSE_FILE ]]; then + echo "Pre-trained model not available. Attempting download" + HUBDIR=$(dirname $(dirname $MODEL_LICENSE_FILE)) + mkdir -p "$(dirname $MODEL_WEIGHTS_FILE)" + wget https://dl.fbaipublicfiles.com/dinov2/dinov2_vits14/dinov2_vits14_pretrain.pth -O "$MODEL_WEIGHTS_FILE" + wget https://github.com/facebookresearch/dinov2/archive/refs/heads/main.zip -O /tmp/dinov2.zip + unzip /tmp/dinov2.zip -d "$HUBDIR" + mv "$HUBDIR/dinov2-main" "$HUBDIR/$(basename $(dirname $MODEL_LICENSE_FILE))" + touch "$HUBDIR/trusted_list" + fi + + if echo 2e405cee1bad14912278296d4f42e993 $MODEL_WEIGHTS_FILE | md5sum --check - \ + && echo 153d2db1c329326a2d9f881317ea942e $MODEL_LICENSE_FILE | md5sum --check -; then + cp -r ./docker_config/torch_home_cache "$CLEAN_SOURCE_DIR/torch_home_cache" + chmod a+rX "$CLEAN_SOURCE_DIR/torch_home_cache" -R + else + echo "Model file checksum verification failed" + exit 1 + fi else - exit 1 + echo "Skipping pre-trained model download; not required for Dockerfile_${DOCKERFILE_SUFFIX}" fi -chmod a+rX $CLEAN_SOURCE_DIR/torch_home_cache -R -# build and print follow-up steps +# build the Docker image -docker build $DOCKER_BUILD_ARGS -t $DOCKER_IMAGE $CLEAN_SOURCE_DIR -f docker_config/Dockerfile_ODELIA +docker build $DOCKER_BUILD_ARGS -t "$DOCKER_IMAGE" "$CLEAN_SOURCE_DIR" -f "$DOCKERFILE_PATH" echo "Docker image $DOCKER_IMAGE built successfully" echo "./_buildStartupKits.sh $PROJECT_FILE $VERSION" -./_buildStartupKits.sh $PROJECT_FILE $VERSION +./_buildStartupKits.sh "$PROJECT_FILE" "$VERSION" echo "Startup kits built successfully" rm -rf $CLEAN_SOURCE_DIR diff --git a/docker_config/Dockerfile_stamp b/docker_config/Dockerfile_stamp new file mode 100644 index 00000000..24e5734e --- /dev/null +++ b/docker_config/Dockerfile_stamp @@ -0,0 +1,58 @@ +# Base image with Python 3.11 (CUDA-enabled PyTorch image) +ARG PYTORCH_IMAGE=pytorch/pytorch:2.2.2-cuda12.1-cudnn8-runtime +FROM ${PYTORCH_IMAGE} + +# ------------------ Metadata ------------------ +ARG NVF_VERSION=2.4.1 +ENV NVF_BRANCH=${NVF_VERSION} +ENV PYTHON_VERSION=3.11 +ENV DEBIAN_FRONTEND=noninteractive +ENV PATH="/workspace/STAMP/.venv/bin:$PATH" +ENV HF_HOME=/workspace/.hf_cache + +# ------------------ System Dependencies ------------------ +RUN apt-get update && apt-get install -y --no-install-recommends \ + git curl wget unzip ca-certificates build-essential \ + libglib2.0-dev libgl1 libglx-mesa0 libglib2.0-0 \ + openslide-tools libgl1-mesa-glx \ + python3.11 python3.11-venv python3.11-dev \ + && rm -rf /var/lib/apt/lists/* + +# ------------------ Set Default Python ------------------ +RUN update-alternatives --install /usr/bin/python python /usr/bin/python3.11 1 && \ + update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.11 1 && \ + update-alternatives --install /usr/bin/pip pip /usr/bin/pip3 1 + +# ------------------ Install uv ------------------ +RUN curl -LsSf https://astral.sh/uv/install.sh | sh -s -- --yes + +# ------------------ NVFlare Installation ------------------ +WORKDIR /workspace/ +COPY ./MediSwarm/docker_config/NVFlare /workspace/nvflare +COPY ./MediSwarm/docker_config/master_template.yml /workspace/nvflare/nvflare/lighter/impl/ +RUN python -m pip install /workspace/nvflare && rm -rf /workspace/nvflare + +# ------------------ MediSwarm Controller Installation ------------------ +COPY ./MediSwarm/controller /workspace/controller +RUN python -m pip install /workspace/controller && rm -rf /workspace/controller + +# ------------------ STAMP Installation ------------------ +RUN git clone https://github.com/KatherLab/STAMP.git /workspace/STAMP +WORKDIR /workspace/STAMP +RUN /root/.cargo/bin/uv venv && \ + /root/.cargo/bin/uv pip install --upgrade pip && \ + /root/.cargo/bin/uv sync --all-extras + +# ------------------ Expose stamp as CLI globally ------------------ +RUN ln -s /workspace/STAMP/.venv/bin/stamp /usr/local/bin/stamp + +# ------------------ HuggingFace CLI ------------------ +RUN /workspace/STAMP/.venv/bin/pip install huggingface_hub && \ + /workspace/STAMP/.venv/bin/huggingface-cli login --help || true + +# ------------------ MediSwarm Source + Link ------------------ +COPY ./MediSwarm /MediSwarm +RUN mkdir -p /fl_admin/transfer && ln -s /MediSwarm /fl_admin/transfer/MediSwarm + +# ------------------ Default Workdir ------------------ +WORKDIR /workspace/ From fd7e6f1b5c37c6eb609566be2401a237ea31878b Mon Sep 17 00:00:00 2001 From: GitHub CI Date: Thu, 17 Jul 2025 15:11:51 +0200 Subject: [PATCH 2/9] feat: add initial implementation of VaryPrecisionTransform and related configurations Signed-off-by: GitHub CI --- application/jobs/stamp/app/custom/main.py | 60 +++ .../stamp/app/custom/modeling/__init__.py | 0 .../jobs/stamp/app/custom/modeling/alibi.py | 147 ++++++ .../jobs/stamp/app/custom/modeling/config.py | 62 +++ .../stamp/app/custom/modeling/crossval.py | 225 +++++++++ .../jobs/stamp/app/custom/modeling/data.py | 443 ++++++++++++++++++ .../jobs/stamp/app/custom/modeling/deploy.py | 217 +++++++++ .../app/custom/modeling/lightning_model.py | 222 +++++++++ .../jobs/stamp/app/custom/modeling/train.py | 319 +++++++++++++ .../stamp/app/custom/modeling/transforms.py | 41 ++ .../jobs/stamp/app/custom/modeling/types.py | 55 +++ .../app/custom/modeling/vision_transformer.py | 243 ++++++++++ 12 files changed, 2034 insertions(+) create mode 100644 application/jobs/stamp/app/custom/main.py create mode 100755 application/jobs/stamp/app/custom/modeling/__init__.py create mode 100644 application/jobs/stamp/app/custom/modeling/alibi.py create mode 100644 application/jobs/stamp/app/custom/modeling/config.py create mode 100644 application/jobs/stamp/app/custom/modeling/crossval.py create mode 100755 application/jobs/stamp/app/custom/modeling/data.py create mode 100644 application/jobs/stamp/app/custom/modeling/deploy.py create mode 100644 application/jobs/stamp/app/custom/modeling/lightning_model.py create mode 100644 application/jobs/stamp/app/custom/modeling/train.py create mode 100644 application/jobs/stamp/app/custom/modeling/transforms.py create mode 100644 application/jobs/stamp/app/custom/modeling/types.py create mode 100755 application/jobs/stamp/app/custom/modeling/vision_transformer.py diff --git a/application/jobs/stamp/app/custom/main.py b/application/jobs/stamp/app/custom/main.py new file mode 100644 index 00000000..df9e75fe --- /dev/null +++ b/application/jobs/stamp/app/custom/main.py @@ -0,0 +1,60 @@ +import logging +import os +import sys +from pathlib import Path + +from modeling.train import train_categorical_model_ + +# Set up the logger +_logger = logging.getLogger("stamp") +_logger.setLevel(logging.DEBUG) +_formatter = logging.Formatter("%(asctime)s\t%(levelname)s\t%(message)s") + +_stream_handler = logging.StreamHandler(sys.stderr) +_stream_handler.setLevel(logging.INFO) +_stream_handler.setFormatter(_formatter) +_logger.addHandler(_stream_handler) + + +def _add_file_handle_(logger: logging.Logger, *, output_dir: Path) -> None: + output_dir.mkdir(exist_ok=True, parents=True) + + file_handler = logging.FileHandler(output_dir / "logfile.log") + file_handler.setLevel(logging.DEBUG) + + formatter = logging.Formatter("%(asctime)s\t%(levelname)s\t%(message)s") + file_handler.setFormatter(formatter) + + logger.addHandler(file_handler) + + +def main(): + output_dir = os.getenv("TRAINING_OUTPUT_DIR") + _add_file_handle_(_logger, output_dir=Path(output_dir)) + _logger.info("Using training configuration from environment variables.") + + train_categorical_model_( + output_dir=Path(output_dir), + clini_table=Path(os.getenv("TRAINING_CLINI_TABLE")), + slide_table=Path(os.getenv("TRAINING_SLIDE_TABLE")), + feature_dir=Path(os.getenv("TRAINING_FEATURE_DIR")), + patient_label=os.getenv("TRAINING_PATIENT_LABEL"), + ground_truth_label=os.getenv("TRAINING_GROUND_TRUTH_LABEL"), + filename_label=os.getenv("TRAINING_FILENAME_LABEL"), + categories=os.getenv("TRAINING_CATEGORIES").split(","), + # Dataset and loader parameters + bag_size=int(os.getenv("TRAINING_BAG_SIZE")), + num_workers=int(os.getenv("TRAINING_NUM_WORKERS")), + # Training parameters + batch_size=int(os.getenv("TRAINING_BATCH_SIZE")), + max_epochs=int(os.getenv("TRAINING_MAX_EPOCHS")), + patience=int(os.getenv("TRAINING_PATIENCE")), + accelerator=os.getenv("TRAINING_ACCELERATOR"), + # Experimental features + use_vary_precision_transform=os.getenv("USE_VARY_PRECISION_TRANSFORM", "False").lower() == "true", + use_alibi=os.getenv("USE_ALIBI", "False").lower() == "true", + ) + + +if __name__ == "__main__": + main() diff --git a/application/jobs/stamp/app/custom/modeling/__init__.py b/application/jobs/stamp/app/custom/modeling/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/application/jobs/stamp/app/custom/modeling/alibi.py b/application/jobs/stamp/app/custom/modeling/alibi.py new file mode 100644 index 00000000..69c61aed --- /dev/null +++ b/application/jobs/stamp/app/custom/modeling/alibi.py @@ -0,0 +1,147 @@ +import torch +from jaxtyping import Bool, Float +from torch import Tensor, nn + + +class _RunningMeanScaler(nn.Module): + """Scales values by the inverse of the mean of values seen before.""" + + def __init__(self, dtype=torch.float32) -> None: + super().__init__() + self.running_mean = nn.Buffer(torch.ones(1, dtype=dtype)) + self.items_so_far = nn.Buffer(torch.ones(1, dtype=dtype)) + + def forward(self, x: torch.Tensor) -> torch.Tensor: + if self.training: + # Welford's algorithm + self.running_mean.copy_( + (self.running_mean + (x - self.running_mean) / self.items_so_far).mean() + ) + self.items_so_far += 1 + + return x / self.running_mean + + +class _ALiBi(nn.Module): + # See MultiHeadAliBi + def __init__(self) -> None: + super().__init__() + + self.scale_distance = _RunningMeanScaler() + self.bias_scale = nn.Parameter(torch.rand(1)) + + def forward( + self, + *, + q: Float[Tensor, "batch query qk_feature"], + k: Float[Tensor, "batch key qk_feature"], + v: Float[Tensor, "batch key v_feature"], + coords_q: Float[Tensor, "batch query coord"], + coords_k: Float[Tensor, "batch key coord"], + attn_mask: Bool[Tensor, "batch query key"] | None, + alibi_mask: Bool[Tensor, "batch query key"] | None, + ) -> Float[Tensor, "batch query v_feature"]: + """ + Args: + alibi_mask: + Which query-key pairs to mask from ALiBi (i.e. don't apply ALiBi to). + """ + weight_logits = torch.einsum("bqf,bkf->bqk", q, k) * (k.size(-1) ** -0.5) + distances = torch.linalg.norm( + coords_q.unsqueeze(2) - coords_k.unsqueeze(1), dim=-1 + ) + scaled_distances = self.scale_distance(distances) * self.bias_scale + + if alibi_mask is not None: + scaled_distances = scaled_distances.where(~alibi_mask, 0.0) + + weights = torch.softmax(weight_logits, dim=-1) + + if attn_mask is not None: + weights = (weights - scaled_distances).where(~attn_mask, 0.0) + else: + weights = weights - scaled_distances + + attention = torch.einsum("bqk,bkf->bqf", weights, v) + + return attention + + +class MultiHeadALiBi(nn.Module): + """Attention with Linear Biases + + Based on + > PRESS, Ofir; SMITH, Noah A.; LEWIS, Mike. + > Train short, test long: Attention with linear biases enables input length extrapolation. + > arXiv preprint arXiv:2108.12409, 2021. + + Since the distances between in WSIs may be quite large, + we scale the distances by the mean distance seen during training. + """ + + def __init__( + self, + *, + embed_dim: int, + num_heads: int, + ) -> None: + super().__init__() + + if embed_dim % num_heads != 0: + raise ValueError(f"{embed_dim=} has to be divisible by {num_heads=}") + + self.query_encoders = nn.ModuleList( + [ + nn.Linear(in_features=embed_dim, out_features=embed_dim // num_heads) + for _ in range(num_heads) + ] + ) + self.key_encoders = nn.ModuleList( + [ + nn.Linear(in_features=embed_dim, out_features=embed_dim // num_heads) + for _ in range(num_heads) + ] + ) + self.value_encoders = nn.ModuleList( + [ + nn.Linear(in_features=embed_dim, out_features=embed_dim // num_heads) + for _ in range(num_heads) + ] + ) + + self.attentions = nn.ModuleList([_ALiBi() for _ in range(num_heads)]) + + self.fc = nn.Linear(in_features=embed_dim, out_features=embed_dim) + + def forward( + self, + *, + q: Float[Tensor, "batch query mh_qk_feature"], + k: Float[Tensor, "batch key mh_qk_feature"], + v: Float[Tensor, "batch key hm_v_feature"], + coords_q: Float[Tensor, "batch query coord"], + coords_k: Float[Tensor, "batch key coord"], + attn_mask: Bool[Tensor, "batch query key"] | None, + alibi_mask: Bool[Tensor, "batch query key"] | None, + ) -> Float[Tensor, "batch query mh_v_feature"]: + stacked_attentions = torch.stack( + [ + att( + q=q_enc(q), + k=k_enc(k), + v=v_enc(v), + coords_q=coords_q, + coords_k=coords_k, + attn_mask=attn_mask, + alibi_mask=alibi_mask, + ) + for q_enc, k_enc, v_enc, att in zip( + self.query_encoders, + self.key_encoders, + self.value_encoders, + self.attentions, + strict=True, + ) + ] + ) + return self.fc(stacked_attentions.permute(1, 2, 0, 3).flatten(-2, -1)) diff --git a/application/jobs/stamp/app/custom/modeling/config.py b/application/jobs/stamp/app/custom/modeling/config.py new file mode 100644 index 00000000..74f7e429 --- /dev/null +++ b/application/jobs/stamp/app/custom/modeling/config.py @@ -0,0 +1,62 @@ +import os +from pathlib import Path + +import torch +from pydantic import BaseModel, ConfigDict, Field +from stamp.types import PandasLabel + + +class TrainConfig(BaseModel): + model_config = ConfigDict(extra="forbid") + + output_dir: Path = Field(description="The directory to save the results to") + + clini_table: Path = Field(description="Excel or CSV to read clinical data from") + slide_table: Path = Field( + description="Excel or CSV to read patient-slide associations from" + ) + feature_dir: Path = Field(description="Directory containing feature files") + + ground_truth_label: PandasLabel = Field( + description="Name of categorical column in clinical table to train on" + ) + categories: list[str] | None = None + + patient_label: PandasLabel = "PATIENT" + filename_label: PandasLabel = "FILENAME" + + # Dataset and -loader parameters + bag_size: int = 512 + num_workers: int = min(os.cpu_count() or 1, 16) + + # Training paramenters + batch_size: int = 64 + max_epochs: int = 64 + patience: int = 16 + accelerator: str = "gpu" if torch.cuda.is_available() else "cpu" + + # Experimental features + use_vary_precision_transform: bool = False + use_alibi: bool = False + + +class CrossvalConfig(TrainConfig): + n_splits: int = Field(5, ge=2) + + +class DeploymentConfig(BaseModel): + model_config = ConfigDict(extra="forbid") + + output_dir: Path + + checkpoint_paths: list[Path] + clini_table: Path | None = None + slide_table: Path + feature_dir: Path + + ground_truth_label: PandasLabel | None = None + patient_label: PandasLabel = "PATIENT" + filename_label: PandasLabel = "FILENAME" + + num_workers: int = min(os.cpu_count() or 1, 16) + accelerator: str = "gpu" if torch.cuda.is_available() else "cpu" diff --git a/application/jobs/stamp/app/custom/modeling/crossval.py b/application/jobs/stamp/app/custom/modeling/crossval.py new file mode 100644 index 00000000..4ff21ce7 --- /dev/null +++ b/application/jobs/stamp/app/custom/modeling/crossval.py @@ -0,0 +1,225 @@ +import logging +from collections.abc import Mapping, Sequence +from pathlib import Path +from typing import Any, Final + +import numpy as np +from lightning.pytorch.accelerators.accelerator import Accelerator +from pydantic import BaseModel +from sklearn.model_selection import StratifiedKFold +from stamp.modeling.data import ( + PatientData, + filter_complete_patient_data_, + patient_to_ground_truth_from_clini_table_, + slide_to_patient_from_slide_table_, +) +from stamp.modeling.deploy import _predict, _to_prediction_df +from stamp.modeling.lightning_model import LitVisionTransformer +from stamp.modeling.train import setup_model_for_training, train_model_ +from stamp.modeling.transforms import VaryPrecisionTransform +from stamp.types import ( + Category, + FeaturePath, + GroundTruth, + PandasLabel, + PatientId, +) + +__author__ = "Marko van Treeck" +__copyright__ = "Copyright (C) 2024 Marko van Treeck" +__license__ = "MIT" + +_logger = logging.getLogger("stamp") + + +class _Split(BaseModel): + train_patients: set[PatientId] + test_patients: set[PatientId] + + +class _Splits(BaseModel): + splits: Sequence[_Split] + + +def categorical_crossval_( + clini_table: Path, + slide_table: Path, + feature_dir: Path, + output_dir: Path, + patient_label: PandasLabel, + ground_truth_label: PandasLabel, + filename_label: PandasLabel, + categories: Sequence[Category] | None, + n_splits: int, + # Dataset and -loader parameters + bag_size: int, + num_workers: int, + # Training paramenters + batch_size: int, + max_epochs: int, + patience: int, + accelerator: str | Accelerator, + # Experimental features + use_vary_precision_transform: bool, + use_alibi: bool, +) -> None: + patient_to_ground_truth: Final[dict[PatientId, GroundTruth]] = ( + patient_to_ground_truth_from_clini_table_( + clini_table_path=clini_table, + ground_truth_label=ground_truth_label, + patient_label=patient_label, + ) + ) + slide_to_patient: Final[dict[FeaturePath, PatientId]] = ( + slide_to_patient_from_slide_table_( + slide_table_path=slide_table, + feature_dir=feature_dir, + patient_label=patient_label, + filename_label=filename_label, + ) + ) + + # Clean data (remove slides without ground truth, missing features, etc.) + patient_to_data: Final[Mapping[Category, PatientData]] = ( + filter_complete_patient_data_( + patient_to_ground_truth=patient_to_ground_truth, + slide_to_patient=slide_to_patient, + drop_patients_with_missing_ground_truth=True, + ) + ) + + output_dir.mkdir(parents=True, exist_ok=True) + splits_file = output_dir / "splits.json" + + # Generate the splits, or load them from the splits file if they already exist + if not splits_file.exists(): + splits = _get_splits(patient_to_data=patient_to_data, n_splits=n_splits) + with open(splits_file, "w") as fp: + fp.write(splits.model_dump_json(indent=4)) + else: + _logger.debug(f"reading splits from {splits_file}") + with open(splits_file, "r") as fp: + splits = _Splits.model_validate_json(fp.read()) + + patients_in_splits = { + patient + for split in splits.splits + for patient in [*split.train_patients, *split.test_patients] + } + + if patients_without_ground_truth := patients_in_splits - patient_to_data.keys(): + raise RuntimeError( + "The splits file contains some patients we don't have information for in the clini / slide table: " + f"{patients_without_ground_truth}" + ) + + if ground_truths_not_in_split := patient_to_data.keys() - patients_in_splits: + _logger.warning( + "Some of the entries in the clini / slide table are not in the crossval split: " + f"{ground_truths_not_in_split}" + ) + + categories = categories or sorted( + { + patient_data.ground_truth + for patient_data in patient_to_data.values() + if patient_data.ground_truth is not None + } + ) + + for split_i, split in enumerate(splits.splits): + split_dir = output_dir / f"split-{split_i}" + + if (split_dir / "patient-preds.csv").exists(): + _logger.info( + f"skipping training for split {split_i}, " + "as a model checkpoint is already present" + ) + continue + + # Train the model + if not (split_dir / "model.ckpt").exists(): + model, train_dl, valid_dl = setup_model_for_training( + clini_table=clini_table, + slide_table=slide_table, + feature_dir=feature_dir, + ground_truth_label=ground_truth_label, + bag_size=bag_size, + num_workers=num_workers, + batch_size=batch_size, + patient_to_data={ + patient_id: patient_data + for patient_id, patient_data in patient_to_data.items() + if patient_id in split.train_patients + }, + categories=( + categories + or sorted( + { + patient_data.ground_truth + for patient_data in patient_to_data.values() + if patient_data.ground_truth is not None + } + ) + ), + train_transform=( + VaryPrecisionTransform(min_fraction_bits=1) + if use_vary_precision_transform + else None + ), + use_alibi=use_alibi, + ) + model = train_model_( + output_dir=split_dir, + model=model, + train_dl=train_dl, + valid_dl=valid_dl, + max_epochs=max_epochs, + patience=patience, + accelerator=accelerator, + ) + else: + model = LitVisionTransformer.load_from_checkpoint(split_dir / "model.ckpt") + + # Deploy on test set + if not (split_dir / "patient-preds.csv").exists(): + predictions = _predict( + model=model, + patient_to_data={ + patient_id: patient_data + for patient_id, patient_data in patient_to_data.items() + if patient_id in split.test_patients + }, + num_workers=num_workers, + accelerator=accelerator, + ) + + _to_prediction_df( + categories=categories, + patient_to_ground_truth=patient_to_ground_truth, + predictions=predictions, + patient_label=patient_label, + ground_truth_label=ground_truth_label, + ).to_csv(split_dir / "patient-preds.csv", index=False) + + +def _get_splits( + *, patient_to_data: Mapping[PatientId, PatientData[Any]], n_splits: int +) -> _Splits: + patients = np.array(list(patient_to_data.keys())) + skf = StratifiedKFold(n_splits=n_splits, shuffle=True, random_state=0) + splits = _Splits( + splits=[ + _Split( + train_patients=set(patients[train_indices]), + test_patients=set(patients[test_indices]), + ) + for train_indices, test_indices in skf.split( + patients, + np.array( + [patient.ground_truth for patient in patient_to_data.values()] + ), + ) + ] + ) + return splits diff --git a/application/jobs/stamp/app/custom/modeling/data.py b/application/jobs/stamp/app/custom/modeling/data.py new file mode 100755 index 00000000..10b157f4 --- /dev/null +++ b/application/jobs/stamp/app/custom/modeling/data.py @@ -0,0 +1,443 @@ +"""Helper classes to manage pytorch data.""" + +import logging +from collections.abc import Callable, Iterable, Mapping, Sequence +from dataclasses import KW_ONLY, dataclass +from itertools import groupby +from pathlib import Path +from typing import BinaryIO, Generic, TextIO, TypeAlias, cast + +import h5py +import numpy as np +import pandas as pd +import stamp +import torch +from jaxtyping import Bool, Float +from packaging.version import Version +from stamp.types import ( + Bags, + BagSize, + BagSizes, + Category, + CoordinatesBatch, + EncodedTargets, + FeaturePath, + GroundTruth, + GroundTruthType, + Microns, + PandasLabel, + PatientId, + SlideMPP, + TilePixels, +) +from torch import Tensor +from torch.utils.data import DataLoader, Dataset + +_logger = logging.getLogger("stamp") + +__author__ = "Marko van Treeck" +__copyright__ = "Copyright (C) 2022-2025 Marko van Treeck" +__license__ = "MIT" + +_Bag: TypeAlias = Float[Tensor, "tile feature"] +_EncodedTarget: TypeAlias = Bool[Tensor, "category_is_hot"] # noqa: F821 +"""The ground truth, encoded numerically (currently: one-hot)""" +_Coordinates: TypeAlias = Float[Tensor, "tile 2"] + + +@dataclass +class PatientData(Generic[GroundTruthType]): + """All raw (i.e. non-generated) information we have on the patient.""" + + _ = KW_ONLY + ground_truth: GroundTruthType + feature_files: Iterable[FeaturePath | BinaryIO] + + +def dataloader_from_patient_data( + *, + patient_data: Sequence[PatientData[GroundTruth | None]], + bag_size: int | None, + categories: Sequence[Category] | None = None, + batch_size: int, + shuffle: bool, + num_workers: int, + transform: Callable[[Tensor], Tensor] | None, +) -> tuple[ + DataLoader[tuple[Bags, CoordinatesBatch, BagSizes, EncodedTargets]], + Sequence[Category], +]: + """Creates a dataloader from patient data, encoding the ground truths. + + Args: + categories: + Order of classes for one-hot encoding. + If `None`, classes are inferred from patient data. + """ + + raw_ground_truths = np.array([patient.ground_truth for patient in patient_data]) + categories = ( + categories if categories is not None else list(np.unique(raw_ground_truths)) + ) + one_hot = torch.tensor(raw_ground_truths.reshape(-1, 1) == categories) + ds = BagDataset( + bags=[patient.feature_files for patient in patient_data], + bag_size=bag_size, + ground_truths=one_hot, + transform=transform, + ) + + return ( + cast( + DataLoader[tuple[Bags, CoordinatesBatch, BagSizes, EncodedTargets]], + DataLoader( + ds, + batch_size=batch_size, + shuffle=shuffle, + num_workers=num_workers, + collate_fn=_collate_to_tuple, + ), + ), + list(categories), + ) + + +def _collate_to_tuple( + items: list[tuple[_Bag, _Coordinates, BagSize, _EncodedTarget]], +) -> tuple[Bags, CoordinatesBatch, BagSizes, EncodedTargets]: + bags = torch.stack([bag for bag, _, _, _ in items]) + coords = torch.stack([coord for _, coord, _, _ in items]) + bag_sizes = torch.tensor([bagsize for _, _, bagsize, _ in items]) + encoded_targets = torch.stack([encoded_target for _, _, _, encoded_target in items]) + + return (bags, coords, bag_sizes, encoded_targets) + + +@dataclass +class BagDataset(Dataset[tuple[_Bag, _Coordinates, BagSize, _EncodedTarget]]): + """A dataset of bags of instances.""" + + _: KW_ONLY + bags: Sequence[Iterable[FeaturePath | BinaryIO]] + """The `.h5` files containing the bags. + + Each bag consists of the features taken from one or multiple h5 files. + Each of the h5 files needs to have a dataset called `feats` of shape N x F, + where N is the number of instances and F the number of features per instance. + """ + + bag_size: BagSize | None = None + """The number of instances in each bag. + + For bags containing more instances, + a random sample of `bag_size` instances will be drawn. + Smaller bags are padded with zeros. + If `bag_size` is None, all the samples will be used. + """ + + ground_truths: Bool[Tensor, "index category_is_hot"] + """The ground truth for each bag, one-hot encoded.""" + + transform: Callable[[Tensor], Tensor] | None + + def __post_init__(self) -> None: + if len(self.bags) != len(self.ground_truths): + raise ValueError( + "the number of ground truths has to match the number of bags" + ) + + def __len__(self) -> int: + return len(self.bags) + + def __getitem__( + self, index: int + ) -> tuple[_Bag, _Coordinates, BagSize, _EncodedTarget]: + # Collect all the features + feats = [] + coords_um = [] + for bag_file in self.bags[index]: + with h5py.File(bag_file, "r") as h5: + feats.append( + torch.from_numpy(h5["feats"][:]) # pyright: ignore[reportIndexIssue] + ) + coords_um.append(torch.from_numpy(get_coords(h5).coords_um)) + + feats = torch.concat(feats).float() + coords_um = torch.concat(coords_um).float() + + if self.transform is not None: + feats = self.transform(feats) + + # Sample a subset, if required + if self.bag_size is not None: + return ( + *_to_fixed_size_bag(feats, coords=coords_um, bag_size=self.bag_size), + self.ground_truths[index], + ) + else: + return ( + feats, + coords_um, + len(feats), + self.ground_truths[index], + ) + + +@dataclass +class CoordsInfo: + coords_um: np.ndarray + tile_size_um: Microns + tile_size_px: TilePixels | None = None + + @property + def mpp(self) -> SlideMPP: + if not self.tile_size_px: + raise RuntimeError( + "tile size in pixels is not available. Please reextract them using `stamp preprocess`." + ) + return SlideMPP(self.tile_size_um / self.tile_size_px) + + +def get_coords(feature_h5: h5py.File) -> CoordsInfo: + coords: np.ndarray = feature_h5["coords"][:] # type: ignore + coords_um: np.ndarray | None = None + tile_size_um: Microns | None = None + tile_size_px: TilePixels | None = None + if (tile_size := feature_h5.attrs.get("tile_size", None)) and feature_h5.attrs.get( + "unit", None + ) == "um": + # STAMP v2 format + tile_size_um = Microns(float(tile_size)) + coords_um = coords + elif tile_size := feature_h5.attrs.get("tile_size_um", None): + # Newer STAMP format + tile_size_um = Microns(float(tile_size)) + coords_um = coords + elif ( + round( + feature_h5.attrs.get( + "tile_size", get_stride(torch.from_numpy(coords).float()) + ) + ) + == 224 + ): + # Historic STAMP format + _logger.info( + f"{feature_h5.filename}: tile stride is roughly 224, assuming coordinates have unit 256um/224px (historic STAMP format)" + ) + tile_size_um = Microns(256.0) + tile_size_px = TilePixels(224) + coords_um = coords / 224 * 256 + + if (version_str := feature_h5.attrs.get("stamp_version")) and ( + extraction_version := Version(version_str) + ) > Version(stamp.__version__): + raise RuntimeError( + f"features were extracted with a newer version of stamp, please update your stamp to at least version {extraction_version}." + ) + + if not tile_size_px and "tile_size_px" in feature_h5.attrs: + tile_size_px = TilePixels(int(feature_h5.attrs["tile_size_px"])) # pyright: ignore[reportArgumentType] + + if not tile_size_um or coords_um is None: + raise RuntimeError( + "unable to infer coordinates from feature file. Please reextract them using `stamp preprocess`." + ) + + return CoordsInfo(coords_um, tile_size_um, tile_size_px) + + +def _to_fixed_size_bag( + bag: _Bag, coords: _Coordinates, bag_size: BagSize +) -> tuple[_Bag, _Coordinates, BagSize]: + """Samples a fixed-size bag of tiles from an arbitrary one. + + If the original bag did not have enough tiles, + the bag is zero-padded to the right. + """ + # get up to bag_size elements + n_tiles, _dim_feats = bag.shape + bag_idxs = torch.randperm(n_tiles)[:bag_size] + bag_samples = bag[bag_idxs] + coord_samples = coords[bag_idxs] + + # zero-pad if we don't have enough samples + zero_padded_bag = torch.cat( + ( + bag_samples, + torch.zeros(bag_size - bag_samples.shape[0], bag_samples.shape[1]), + ) + ) + zero_padded_coord = torch.cat( + ( + coord_samples, + torch.zeros(bag_size - coord_samples.shape[0], coord_samples.shape[1]), + ) + ) + return zero_padded_bag, zero_padded_coord, min(bag_size, len(bag)) + + +def patient_to_ground_truth_from_clini_table_( + *, + clini_table_path: Path | TextIO, + patient_label: PandasLabel, + ground_truth_label: PandasLabel, +) -> dict[PatientId, GroundTruth]: + """Loads the patients and their ground truths from a clini table.""" + clini_df = _read_table( + clini_table_path, + usecols=[patient_label, ground_truth_label], + dtype=str, + ).dropna() + try: + patient_to_ground_truth: Mapping[PatientId, GroundTruth] = clini_df.set_index( + patient_label, verify_integrity=True + )[ground_truth_label].to_dict() + except KeyError as e: + if patient_label not in clini_df: + raise ValueError( + f"{patient_label} was not found in clini table " + f"(columns in clini table: {clini_df.columns})" + ) from e + elif ground_truth_label not in clini_df: + raise ValueError( + f"{ground_truth_label} was not found in clini table " + f"(columns in clini table: {clini_df.columns})" + ) from e + else: + raise e from e + + return patient_to_ground_truth + + +def slide_to_patient_from_slide_table_( + *, + slide_table_path: Path, + feature_dir: Path, + patient_label: PandasLabel, + filename_label: PandasLabel, +) -> dict[FeaturePath, PatientId]: + """Creates a slide-to-patient mapping from a slide table.""" + slide_df = _read_table( + slide_table_path, + usecols=[patient_label, filename_label], + dtype=str, + ) + + slide_to_patient: Mapping[FeaturePath, PatientId] = { + FeaturePath(feature_dir / cast(str, k)): PatientId(cast(str, patient)) + for k, patient in slide_df.set_index(filename_label, verify_integrity=True)[ + patient_label + ].items() + } + + return slide_to_patient + + +def _read_table(path: Path | TextIO, **kwargs) -> pd.DataFrame: + if not isinstance(path, Path): + return pd.read_csv(path, **kwargs) + elif path.suffix == ".xlsx": + return pd.read_excel(path, **kwargs) + elif path.suffix == ".csv": + return pd.read_csv(path, **kwargs) + else: + raise ValueError( + "table to load has to either be an excel (`*.xlsx`) or csv (`*.csv`) file." + ) + + +def filter_complete_patient_data_( + *, + patient_to_ground_truth: Mapping[PatientId, GroundTruth | None], + slide_to_patient: Mapping[FeaturePath, PatientId], + drop_patients_with_missing_ground_truth: bool, +) -> Mapping[PatientId, PatientData]: + """Aggregate information for all patients for which we have complete data. + + This will sort out slides with missing ground truth, missing features, etc. + Patients with their ground truth set explicitly set to `None` will be _included_. + + Side effects: + Checks feature paths' existance. + """ + + _log_patient_slide_feature_inconsistencies( + patient_to_ground_truth=patient_to_ground_truth, + slide_to_patient=slide_to_patient, + ) + + patient_to_slides: dict[PatientId, set[FeaturePath]] = { + patient: set(slides) + for patient, slides in groupby( + slide_to_patient, lambda slide: slide_to_patient[slide] + ) + } + + if not drop_patients_with_missing_ground_truth: + patient_to_ground_truth = { + **{patient_id: None for patient_id in patient_to_slides}, + **patient_to_ground_truth, + } + + patients = { + patient_id: PatientData( + ground_truth=ground_truth, feature_files=existing_features_for_patient + ) + for patient_id, ground_truth in patient_to_ground_truth.items() + # Restrict to only patients which have slides and features + if (slides := patient_to_slides.get(patient_id)) is not None + and ( + existing_features_for_patient := { + feature_path for feature_path in slides if feature_path.exists() + } + ) + } + + return patients + + +def _log_patient_slide_feature_inconsistencies( + *, + patient_to_ground_truth: Mapping[PatientId, GroundTruthType], + slide_to_patient: Mapping[FeaturePath, PatientId], +) -> None: + """Checks whether the arguments are consistent and logs all irregularities. + + Has no side effects outside of logging. + """ + if ( + patients_without_slides := patient_to_ground_truth.keys() + - slide_to_patient.values() + ): + _logger.warning( + f"some patients have no associated slides: {patients_without_slides}" + ) + + if patients_without_ground_truth := ( + slide_to_patient.values() - patient_to_ground_truth.keys() + ): + _logger.warning( + f"some patients have no clinical information: {patients_without_ground_truth}" + ) + + if slides_without_features := { + slide for slide in slide_to_patient.keys() if not slide.exists() + }: + _logger.warning( + f"some feature files could not be found: {slides_without_features}" + ) + + +def get_stride(coords: Float[Tensor, "tile 2"]) -> float: + """Gets the minimum step width between any two coordintes.""" + xs: Tensor = coords[:, 0].unique(sorted=True) + ys: Tensor = coords[:, 1].unique(sorted=True) + stride = cast( + float, + min( + (xs[1:] - xs[:-1]).min().item(), + (ys[1:] - ys[:-1]).min().item(), + ), + ) + return stride diff --git a/application/jobs/stamp/app/custom/modeling/deploy.py b/application/jobs/stamp/app/custom/modeling/deploy.py new file mode 100644 index 00000000..f0cda653 --- /dev/null +++ b/application/jobs/stamp/app/custom/modeling/deploy.py @@ -0,0 +1,217 @@ +import logging +from collections.abc import Mapping, Sequence +from pathlib import Path +from typing import TypeAlias, cast + +import lightning +import numpy as np +import pandas as pd +import torch +from jaxtyping import Float +from lightning.pytorch.accelerators.accelerator import Accelerator +from stamp.modeling.data import ( + PatientData, + dataloader_from_patient_data, + filter_complete_patient_data_, + patient_to_ground_truth_from_clini_table_, + slide_to_patient_from_slide_table_, +) +from stamp.modeling.lightning_model import LitVisionTransformer +from stamp.types import GroundTruth, PandasLabel, PatientId + +__all__ = ["deploy_categorical_model_"] + +__author__ = "Marko van Treeck" +__copyright__ = "Copyright (C) 2024-2025 Marko van Treeck" +__license__ = "MIT" + +_logger = logging.getLogger("stamp") + +Logit: TypeAlias = float + + +def deploy_categorical_model_( + *, + output_dir: Path, + checkpoint_paths: Sequence[Path], + clini_table: Path | None, + slide_table: Path, + feature_dir: Path, + ground_truth_label: PandasLabel | None, + patient_label: PandasLabel, + filename_label: PandasLabel, + num_workers: int, + accelerator: str | Accelerator, +) -> None: + models = [ + LitVisionTransformer.load_from_checkpoint( + checkpoint_path=checkpoint_path + ).eval() + for checkpoint_path in checkpoint_paths + ] + + # Ensure all models were trained on the same ground truth label + if ( + len(ground_truth_labels := set(model.ground_truth_label for model in models)) + != 1 + ): + raise RuntimeError( + f"ground truth labels differ between models: {ground_truth_labels}" + ) + # Ensure the categories were the same between all models + if len(categories := set(tuple(model.categories) for model in models)) != 1: + raise RuntimeError(f"categories differ between models: {categories}") + + model_ground_truth_label = models[0].ground_truth_label + model_categories = list(models[0].categories) + + if ( + ground_truth_label is not None + and ground_truth_label != model_ground_truth_label + ): + _logger.warning( + "deployment ground truth label differs from training: " + f"{ground_truth_label} vs {model_ground_truth_label}" + ) + ground_truth_label = ground_truth_label or model_ground_truth_label + + output_dir.mkdir(exist_ok=True, parents=True) + + slide_to_patient = slide_to_patient_from_slide_table_( + slide_table_path=slide_table, + feature_dir=feature_dir, + patient_label=patient_label, + filename_label=filename_label, + ) + + patient_to_ground_truth: Mapping[PatientId, GroundTruth | None] + if clini_table is not None: + patient_to_ground_truth = patient_to_ground_truth_from_clini_table_( + clini_table_path=clini_table, + ground_truth_label=ground_truth_label, + patient_label=patient_label, + ) + else: + patient_to_ground_truth = { + patient_id: None for patient_id in set(slide_to_patient.values()) + } + + patient_to_data = filter_complete_patient_data_( + patient_to_ground_truth=patient_to_ground_truth, + slide_to_patient=slide_to_patient, + drop_patients_with_missing_ground_truth=False, + ) + + all_predictions: list[Mapping[PatientId, Float[torch.Tensor, "category"]]] = [] # noqa: F821 + for model_i, model in enumerate(models): + predictions = _predict( + model=model, + patient_to_data=patient_to_data, + num_workers=num_workers, + accelerator=accelerator, + ) + all_predictions.append(predictions) + + _to_prediction_df( + categories=model_categories, + patient_to_ground_truth=patient_to_ground_truth, + predictions=predictions, + patient_label=patient_label, + ground_truth_label=ground_truth_label, + ).to_csv(output_dir / f"patient-preds-{model_i}.csv", index=False) + + # TODO we probably also want to save the 95% confidence interval in addition to the mean + _to_prediction_df( + categories=model_categories, + patient_to_ground_truth=patient_to_ground_truth, + predictions={ + # Mean prediction + patient_id: torch.stack( + [predictions[patient_id] for predictions in all_predictions] + ).mean(dim=0) + for patient_id in patient_to_data.keys() + }, + patient_label=patient_label, + ground_truth_label=ground_truth_label, + ).to_csv(output_dir / "patient-preds.csv", index=False) + + +def _predict( + *, + model: LitVisionTransformer, + patient_to_data: Mapping[PatientId, PatientData[GroundTruth | None]], + num_workers: int, + accelerator: str | Accelerator, +) -> Mapping[PatientId, Float[torch.Tensor, "category"]]: # noqa: F821 + model = model.eval() + torch.set_float32_matmul_precision("medium") + + patients_used_for_training: set[PatientId] = set(model.train_patients) | set( + model.valid_patients + ) + if overlap := patients_used_for_training & set(patient_to_data.keys()): + raise ValueError( + f"some of the patients in the validation set were used during training: {overlap}" + ) + + test_dl, _ = dataloader_from_patient_data( + patient_data=list(patient_to_data.values()), + bag_size=None, # Use all the tiles for deployment + # Use same encoding scheme as during training + categories=list(model.categories), + batch_size=1, + shuffle=False, + num_workers=num_workers, + transform=None, + ) + + trainer = lightning.Trainer( + accelerator=accelerator, + devices=1, # Needs to be 1, otherwise half the predictions are missing for some reason + logger=False, + ) + predictions = torch.softmax( + torch.concat( + cast( + list[torch.Tensor], + trainer.predict(model, test_dl), + ) + ), + dim=1, + ) + + return dict(zip(patient_to_data, predictions, strict=True)) + + +def _to_prediction_df( + *, + categories: Sequence[GroundTruth], + patient_to_ground_truth: Mapping[PatientId, GroundTruth | None], + predictions: Mapping[PatientId, torch.Tensor], + patient_label: PandasLabel, + ground_truth_label: PandasLabel, +) -> pd.DataFrame: + """Compiles deployment results into a DataFrame.""" + return pd.DataFrame( + [ + { + patient_label: patient_id, + ground_truth_label: patient_to_ground_truth.get(patient_id), + "pred": categories[int(prediction.argmax())], + **{ + f"{ground_truth_label}_{category}": prediction[i_cat].item() + for i_cat, category in enumerate(categories) + }, + "loss": ( + torch.nn.functional.cross_entropy( + prediction.reshape(1, -1), + torch.tensor(np.where(np.array(categories) == ground_truth)[0]), + ).item() + if (ground_truth := patient_to_ground_truth.get(patient_id)) + is not None + else None + ), + } + for patient_id, prediction in predictions.items() + ] + ).sort_values(by="loss") diff --git a/application/jobs/stamp/app/custom/modeling/lightning_model.py b/application/jobs/stamp/app/custom/modeling/lightning_model.py new file mode 100644 index 00000000..5a98018b --- /dev/null +++ b/application/jobs/stamp/app/custom/modeling/lightning_model.py @@ -0,0 +1,222 @@ +"""Lightning wrapper around the model""" + +from collections.abc import Iterable, Sequence +from typing import TypeAlias + +import lightning +import numpy as np +import torch +from jaxtyping import Bool, Float +from torch import Tensor, nn, optim +from torchmetrics.classification import MulticlassAUROC + +from vision_transformer import VisionTransformer +from .types import ( + Bags, + BagSizes, + Category, + CoordinatesBatch, + EncodedTargets, + PandasLabel, + PatientId, +) + +Loss: TypeAlias = Float[Tensor, ""] + + +class LitVisionTransformer(lightning.LightningModule): + """ + PyTorch Lightning wrapper for the Vision Transformer (ViT) model used in weakly supervised + learning settings, such as Multiple Instance Learning (MIL) for whole-slide images or patch-based data. + + This class encapsulates training, validation, testing, and prediction logic, along with: + - Masking logic that ensures only valid tiles (patches) participate in attention during training. + - AUROC metric tracking during validation for multiclass classification. + - Compatibility checks based on the `stamp` framework version. + - Integration of class imbalance handling through weighted cross-entropy loss. + + The attention mask is applied *only* during training to hide paddings + and is skipped during evaluation and inference for reducing memory usage. + + Args: + categories: List of class labels. + category_weights: Class weights for cross-entropy loss to handle imbalance. + dim_input: Input feature dimensionality per tile. + dim_model: Latent dimensionality used inside the transformer. + dim_feedforward: Dimensionality of the transformer MLP block. + n_heads: Number of self-attention heads. + n_layers: Number of transformer layers. + dropout: Dropout rate used throughout the model. + use_alibi: Whether to use ALiBi-style positional bias in attention (optional). + ground_truth_label: Column name for accessing ground-truth labels from metadata. + train_patients: List of patient IDs used for training. + valid_patients: List of patient IDs used for validation. + stamp_version: Version of the `stamp` framework used during training. + **metadata: Additional metadata to store with the model. + """ + + def __init__( + self, + *, + categories: Sequence[Category], + category_weights: Float[Tensor, "category_weight"], # noqa: F821 + dim_input: int, + dim_model: int, + dim_feedforward: int, + n_heads: int, + n_layers: int, + dropout: float, + # Experimental features + # TODO remove default values for stamp 3; they're only here for backwards compatibility + use_alibi: bool = False, + # Metadata used by other parts of stamp, but not by the model itself + ground_truth_label: PandasLabel, + train_patients: Iterable[PatientId], + valid_patients: Iterable[PatientId], + # stamp_version: Version = Version(stamp.__version__), + # Other metadata + **metadata, + ) -> None: + super().__init__() + + if len(categories) != len(category_weights): + raise ValueError( + "the number of category weights has to match the number of categories!" + ) + + self.vision_transformer = VisionTransformer( + dim_output=len(categories), + dim_input=dim_input, + dim_model=dim_model, + n_layers=n_layers, + n_heads=n_heads, + dim_feedforward=dim_feedforward, + dropout=dropout, + use_alibi=use_alibi, + ) + self.class_weights = category_weights + self.valid_auroc = MulticlassAUROC(len(categories)) + + # Used during deployment + self.ground_truth_label = ground_truth_label + self.categories = np.array(categories) + self.train_patients = train_patients + self.valid_patients = valid_patients + + _ = metadata # unused, but saved in model + + # Check if version is compatible. + # This should only happen when the model is loaded, + # otherwise the default value will make these checks pass. + ''' + if stamp_version < Version("2.0.0.dev8"): + # Update this as we change our model in incompatible ways! + raise ValueError( + f"model has been built with stamp version {stamp_version} " + f"which is incompatible with the current version." + ) + + elif stamp_version > Version(stamp.__version__): + # Let's be strict with models "from the future", + # better fail deadly than have broken results. + raise ValueError( + "model has been built with a stamp version newer than the installed one " + f"({stamp_version} > {stamp.__version__}). " + "Please upgrade stamp to a compatible version." + ) + ''' + self.save_hyperparameters() + + def forward( + self, + bags: Bags, + ) -> Float[Tensor, "batch logit"]: + return self.vision_transformer(bags) + + def _step( + self, + *, + batch: tuple[Bags, CoordinatesBatch, BagSizes, EncodedTargets], + step_name: str, + use_mask: bool, + ) -> Loss: + bags, coords, bag_sizes, targets = batch + + mask = _mask_from_bags(bags=bags, bag_sizes=bag_sizes) if use_mask else None + + logits = self.vision_transformer(bags, coords=coords, mask=mask) + + loss = nn.functional.cross_entropy( + logits, + targets.type_as(logits), + weight=self.class_weights.type_as(logits), + ) + + self.log( + f"{step_name}_loss", + loss, + on_step=False, + on_epoch=True, + prog_bar=True, + sync_dist=True, + ) + + if step_name == "validation": + # TODO this is a bit ugly, we'd like to have `_step` without special cases + self.valid_auroc.update(logits, targets.long().argmax(dim=-1)) + self.log( + f"{step_name}_auroc", + self.valid_auroc, + on_step=False, + on_epoch=True, + sync_dist=True, + ) + + return loss + + def training_step( + self, + batch: tuple[Bags, CoordinatesBatch, BagSizes, EncodedTargets], + batch_idx: int, + ) -> Loss: + return self._step(batch=batch, step_name="training", use_mask=True) + + def validation_step( + self, + batch: tuple[Bags, CoordinatesBatch, BagSizes, EncodedTargets], + batch_idx: int, + ) -> Loss: + return self._step(batch=batch, step_name="validation", use_mask=False) + + def test_step( + self, + batch: tuple[Bags, CoordinatesBatch, BagSizes, EncodedTargets], + batch_idx: int, + ) -> Loss: + return self._step(batch=batch, step_name="test", use_mask=False) + + def predict_step( + self, + batch: tuple[Bags, CoordinatesBatch, BagSizes, EncodedTargets], + batch_idx: int, + ) -> Float[Tensor, "batch logit"]: + bags, coords, bag_sizes, _ = batch + # adding a mask here will *drastically* and *unbearably* increase memory usage + return self.vision_transformer(bags, coords=coords, mask=None) + + def configure_optimizers(self) -> optim.Optimizer: + optimizer = optim.Adam(self.parameters(), lr=1e-3) + return optimizer + + +def _mask_from_bags( + *, + bags: Bags, + bag_sizes: BagSizes, +) -> Bool[Tensor, "batch tile"]: + max_possible_bag_size = bags.size(1) + mask = torch.arange(max_possible_bag_size).type_as(bag_sizes).unsqueeze(0).repeat( + len(bags), 1 + ) >= bag_sizes.unsqueeze(1) + + return mask diff --git a/application/jobs/stamp/app/custom/modeling/train.py b/application/jobs/stamp/app/custom/modeling/train.py new file mode 100644 index 00000000..0d71f0ca --- /dev/null +++ b/application/jobs/stamp/app/custom/modeling/train.py @@ -0,0 +1,319 @@ +import logging +import shutil +from collections.abc import Callable, Mapping, Sequence +from pathlib import Path +from typing import cast + +import lightning +import lightning.pytorch +import lightning.pytorch.accelerators +import lightning.pytorch.accelerators.accelerator +import torch +from lightning.pytorch.accelerators.accelerator import Accelerator +from lightning.pytorch.callbacks import EarlyStopping, ModelCheckpoint +from lightning.pytorch.loggers import CSVLogger +from sklearn.model_selection import train_test_split +from torch.utils.data.dataloader import DataLoader + +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + +import nvflare.client.lightning as flare +import nvflare.client as flare_util + +flare_util.init() +SITE_NAME = flare.get_site_name() + +from data import ( + BagDataset, + PatientData, + dataloader_from_patient_data, + filter_complete_patient_data_, + patient_to_ground_truth_from_clini_table_, + slide_to_patient_from_slide_table_, +) +from lightning_model import ( + Bags, + BagSizes, + EncodedTargets, + LitVisionTransformer, +) +from stamp.modeling.transforms import VaryPrecisionTransform +from stamp.types import Category, CoordinatesBatch, GroundTruth, PandasLabel, PatientId + +__author__ = "Marko van Treeck" +__copyright__ = "Copyright (C) 2024 Marko van Treeck" +__license__ = "MIT" + +_logger = logging.getLogger("stamp") + + +def train_categorical_model_( + *, + clini_table: Path, + slide_table: Path, + feature_dir: Path, + output_dir: Path, + patient_label: PandasLabel, + ground_truth_label: PandasLabel, + filename_label: PandasLabel, + categories: Sequence[Category] | None, + # Dataset and -loader parameters + bag_size: int, + num_workers: int, + # Training paramenters + batch_size: int, + max_epochs: int, + patience: int, + accelerator: str | Accelerator, + # Experimental features + use_vary_precision_transform: bool, + use_alibi: bool, +) -> None: + """Trains a model. + + Args: + clini_table: + An excel or csv file to read the clinical information from. + Must at least have the columns specified in the arguments + + `patient_label` (containing a unique patient ID) + and `ground_truth_label` (containing the ground truth to train for). + slide_table: + An excel or csv file to read the patient-slide associations from. + Must at least have the columns specified in the arguments + `patient_label` (containing the patient ID) + and `filename_label` + (containing a filename relative to `feature_dir` + in which some of the patient's features are stored). + feature_dir: + See `slide_table`. + output_dir: + Path into which to output the artifacts (trained model etc.) + generated during training. + patient_label: + See `clini_table`, `slide_table`. + ground_truth_label: + See `clini_table`. + filename_label: + See `slide_table`. + categories: + Categories of the ground truth. + Set to `None` to automatically infer. + """ + # Read and parse data from out clini and slide table + patient_to_ground_truth = patient_to_ground_truth_from_clini_table_( + clini_table_path=clini_table, + ground_truth_label=ground_truth_label, + patient_label=patient_label, + ) + slide_to_patient = slide_to_patient_from_slide_table_( + slide_table_path=slide_table, + feature_dir=feature_dir, + patient_label=patient_label, + filename_label=filename_label, + ) + + # Clean data (remove slides without ground truth, missing features, etc.) + patient_to_data = filter_complete_patient_data_( + patient_to_ground_truth=patient_to_ground_truth, + slide_to_patient=slide_to_patient, + drop_patients_with_missing_ground_truth=True, + ) + + # Train the model + model, train_dl, valid_dl = setup_model_for_training( + patient_to_data=patient_to_data, + categories=categories, + bag_size=bag_size, + batch_size=batch_size, + num_workers=num_workers, + ground_truth_label=ground_truth_label, + clini_table=clini_table, + slide_table=slide_table, + feature_dir=feature_dir, + train_transform=( + VaryPrecisionTransform(min_fraction_bits=1) + if use_vary_precision_transform + else None + ), + use_alibi=use_alibi, + ) + train_model_( + output_dir=output_dir, + model=model, + train_dl=train_dl, + valid_dl=valid_dl, + max_epochs=max_epochs, + patience=patience, + accelerator=accelerator, + ) + + +def train_model_( + *, + output_dir: Path, + model: LitVisionTransformer, + train_dl: DataLoader[tuple[Bags, CoordinatesBatch, BagSizes, EncodedTargets]], + valid_dl: DataLoader[tuple[Bags, CoordinatesBatch, BagSizes, EncodedTargets]], + max_epochs: int, + patience: int, + accelerator: str | Accelerator, +) -> LitVisionTransformer: + """Trains a model. + + Returns: + The model with the best validation loss during training. + """ + torch.set_float32_matmul_precision("high") + + model_checkpoint = ModelCheckpoint( + monitor="validation_loss", + mode="min", + filename="checkpoint-{epoch:02d}-{validation_loss:0.3f}", + ) + trainer = lightning.Trainer( + default_root_dir=output_dir, + callbacks=[ + EarlyStopping(monitor="validation_loss", mode="min", patience=patience), + model_checkpoint, + ], + + max_epochs=max_epochs, + # FIXME The number of accelerators is currently fixed to one for the + # following reasons: + # 1. `trainer.predict()` does not return any predictions if used with + # the default strategy no multiple GPUs + # 2. `barspoon.model.SafeMulticlassAUROC` breaks on multiple GPUs + accelerator=accelerator, + devices=1, + gradient_clip_val=0.5, + logger=CSVLogger(save_dir=output_dir), + log_every_n_steps=len(train_dl), + ) + + flare.patch(trainer) # Patch trainer to enable swarm learning + logger.info(f"Site name: {SITE_NAME}") + + while flare.is_running(): + input_model = flare.receive() + logger.info(f"Current round: {input_model.current_round}") + trainer.fit( + model=model, + train_dataloaders=train_dl, + val_dataloaders=valid_dl, + ) + shutil.copy(model_checkpoint.best_model_path, output_dir / "model.ckpt") + + return LitVisionTransformer.load_from_checkpoint(model_checkpoint.best_model_path) + + +def setup_model_for_training( + *, + patient_to_data: Mapping[PatientId, PatientData[GroundTruth]], + categories: Sequence[Category] | None, + bag_size: int, + batch_size: int, + num_workers: int, + train_transform: Callable[[torch.Tensor], torch.Tensor] | None, + use_alibi: bool, + # Metadata, has no effect on model training + ground_truth_label: PandasLabel, + clini_table: Path, + slide_table: Path, + feature_dir: Path, +) -> tuple[ + LitVisionTransformer, + DataLoader[tuple[Bags, CoordinatesBatch, BagSizes, EncodedTargets]], + DataLoader[tuple[Bags, CoordinatesBatch, BagSizes, EncodedTargets]], +]: + """Creates a model and dataloaders for training""" + + # Do a stratified train-validation split + ground_truths = [ + patient_data.ground_truth + for patient_data in patient_to_data.values() + if patient_data.ground_truth is not None + ] + + if len(ground_truths) != len(patient_to_data): + raise ValueError( + "patient_to_data must have a ground truth defined for all targets!" + ) + + train_patients, valid_patients = cast( + tuple[Sequence[PatientId], Sequence[PatientId]], + train_test_split( + list(patient_to_data), stratify=ground_truths, shuffle=True, random_state=0 + ), + ) + + train_dl, train_categories = dataloader_from_patient_data( + patient_data=[patient_to_data[patient] for patient in train_patients], + categories=categories, + bag_size=bag_size, + batch_size=batch_size, + shuffle=True, + num_workers=num_workers, + transform=train_transform, + ) + del categories # Let's not accidentally reuse the original categories + valid_dl, _ = dataloader_from_patient_data( + patient_data=[patient_to_data[patient] for patient in valid_patients], + bag_size=None, # Use all the patient data for validation + categories=train_categories, + batch_size=1, + shuffle=False, + num_workers=num_workers, + transform=None, + ) + if overlap := set(train_patients) & set(valid_patients): + raise RuntimeError( + f"unreachable: unexpected overlap between training and validation set: {overlap}" + ) + + # Sample one bag to infer the input dimensions of the model + bags, coords, bag_sizes, targets = cast( + tuple[Bags, CoordinatesBatch, BagSizes, EncodedTargets], next(iter(train_dl)) + ) + _, _, dim_feats = bags.shape + + # Weigh classes inversely to their occurrence + category_counts = cast(BagDataset, train_dl.dataset).ground_truths.sum(dim=0) + cat_ratio_reciprocal = category_counts.sum() / category_counts + category_weights = cat_ratio_reciprocal / cat_ratio_reciprocal.sum() + + if len(train_categories) <= 1: + raise ValueError(f"not enough categories to train on: {train_categories}") + elif any(category_counts < 16): + underpopulated_categories = { + category: count + for category, count in zip(train_categories, category_counts, strict=True) + if count < 16 + } + _logger.warning( + f"Some categories do not have enough samples to meaningfully train a model: {underpopulated_categories}. " + "You may want to consider removing these categories; the model will likely overfit on the few samples available." + ) + + # Train the model + model = LitVisionTransformer( + categories=train_categories, + category_weights=category_weights, + dim_input=dim_feats, + dim_model=512, + dim_feedforward=2048, + n_heads=8, + n_layers=2, + dropout=0.25, + use_alibi=use_alibi, + # Metadata, has no effect on model training + ground_truth_label=ground_truth_label, + train_patients=train_patients, + valid_patients=valid_patients, + clini_table=clini_table, + slide_table=slide_table, + feature_dir=feature_dir, + ) + + return model, train_dl, valid_dl diff --git a/application/jobs/stamp/app/custom/modeling/transforms.py b/application/jobs/stamp/app/custom/modeling/transforms.py new file mode 100644 index 00000000..6fd9755f --- /dev/null +++ b/application/jobs/stamp/app/custom/modeling/transforms.py @@ -0,0 +1,41 @@ +import torch +from jaxtyping import Float + + +def vary_precision( + data: Float[torch.Tensor, "*dims"], *, min_fraction_bits: int +) -> Float[torch.Tensor, "*dims"]: + """Randomly reduces the precision of the tensor's values.""" + if min_fraction_bits < 1: + raise ValueError("min_fraction bits has to be at least 1") + + if data.dtype == torch.float32: + fraction_bits = 23 + mask_dtype = torch.int32 + elif data.dtype == torch.float16: + fraction_bits = 10 + mask_dtype = torch.int16 + elif data.dtype == torch.bfloat16: + fraction_bits = 7 + mask_dtype = torch.int16 + else: + raise NotImplementedError( + f"precision variation not implemented for {data.dtype}" + ) + + no_of_bits_to_mask = torch.randint(0, fraction_bits - min_fraction_bits, data.shape) + mask = (~0 << no_of_bits_to_mask).to(dtype=mask_dtype, device=data.device) + augmented = (data.view(mask_dtype) & mask).view(data.dtype) + return augmented + + +class VaryPrecisionTransform: + """A transform randomly reducing the precision of its inputs.""" + + def __init__(self, *, min_fraction_bits: int = 1) -> None: + self.min_fraction_bits = min_fraction_bits + + def __call__( + self, batch: Float[torch.Tensor, "*dims"] + ) -> Float[torch.Tensor, "*dims"]: + return vary_precision(data=batch, min_fraction_bits=self.min_fraction_bits) diff --git a/application/jobs/stamp/app/custom/modeling/types.py b/application/jobs/stamp/app/custom/modeling/types.py new file mode 100644 index 00000000..4d48293a --- /dev/null +++ b/application/jobs/stamp/app/custom/modeling/types.py @@ -0,0 +1,55 @@ +from pathlib import Path +from typing import ( + Final, + Literal, + NewType, + TypeAlias, + TypeVar, +) + +import torch +from beartype.typing import Mapping +from jaxtyping import Bool, Float, Integer +from torch import Tensor + +# tiling + +ImageExtension: TypeAlias = Literal["png", "jpg"] +EXTENSION_TO_FORMAT: Final[Mapping[ImageExtension, str]] = { + "png": "png", + "jpg": "jpeg", +} + +Microns = NewType("Microns", float) +"""Micrometers, usually referring to the tissue on the slide""" + +SlidePixels = NewType("SlidePixels", int) +"""Pixels of the WSI scan at largest magnification (i.e. coordinates used by OpenSlide)""" + +TilePixels = NewType("TilePixels", int) +"""Pixels after resizing, i.e. how they appear on the final tile""" + +SlideMPP = NewType("SlideMPP", float) + +# modeling + +DeviceLikeType: TypeAlias = str | torch.device | int + +PatientId: TypeAlias = str +GroundTruth: TypeAlias = str +FeaturePath = NewType("FeaturePath", Path) + +Category: TypeAlias = str + +BagSize: TypeAlias = int + +# A batch of the above +Bags: TypeAlias = Float[Tensor, "batch tile feature"] +BagSizes: TypeAlias = Integer[Tensor, "batch"] # noqa: F821 +EncodedTargets: TypeAlias = Bool[Tensor, "batch category_is_hot"] +"""The ground truth, encoded numerically (currently: one-hot)""" +CoordinatesBatch: TypeAlias = Float[Tensor, "batch tile 2"] + +PandasLabel: TypeAlias = str + +GroundTruthType = TypeVar("GroundTruthType", covariant=True) diff --git a/application/jobs/stamp/app/custom/modeling/vision_transformer.py b/application/jobs/stamp/app/custom/modeling/vision_transformer.py new file mode 100755 index 00000000..a2d71c0b --- /dev/null +++ b/application/jobs/stamp/app/custom/modeling/vision_transformer.py @@ -0,0 +1,243 @@ +""" +In parts from https://github.com/lucidrains/vit-pytorch/blob/main/vit_pytorch/vit.py +""" + +from collections.abc import Iterable +from typing import assert_never, cast + +import torch +from beartype import beartype +from einops import repeat +from jaxtyping import Bool, Float, jaxtyped +from stamp.modeling.alibi import MultiHeadALiBi +from torch import Tensor, nn + + +def feed_forward( + dim: int, + hidden_dim: int, + dropout: float = 0.5, +) -> nn.Module: + return nn.Sequential( + nn.LayerNorm(dim), + nn.Linear(dim, hidden_dim), + nn.GELU(), + nn.Dropout(dropout), + nn.Linear(hidden_dim, dim), + nn.Dropout(dropout), + ) + + +class SelfAttention(nn.Module): + def __init__( + self, + *, + dim: int, + num_heads: int, + dropout: float, + use_alibi: bool, + ) -> None: + super().__init__() + self.heads = num_heads + self.norm = nn.LayerNorm(dim) + + if use_alibi: + self.mhsa = MultiHeadALiBi( + embed_dim=dim, + num_heads=num_heads, + ) + else: + self.mhsa = nn.MultiheadAttention(dim, num_heads, dropout, batch_first=True) + + @jaxtyped(typechecker=beartype) + def forward( + self, + x: Float[Tensor, "batch sequence proj_feature"], + *, + coords: Float[Tensor, "batch sequence xy"], + attn_mask: Bool[Tensor, "batch sequence sequence"] | None, + # Help, my abstractions are leaking! + alibi_mask: Bool[Tensor, "batch sequence sequence"] | None, + ) -> Float[Tensor, "batch sequence proj_feature"]: + """ + Args: + attn_mask: + Which of the features to ignore during self-attention. + `attn_mask[b,q,k] == False` means that + query `q` of batch `b` can attend to key `k`. + If `attn_mask` is `None`, all tokens can attend to all others. + alibi_mask: + Which query-key pairs to apply ALiBi to. + If this module was constructed using `use_alibi=False`, + this has no effect. + """ + x = self.norm(x) + match self.mhsa: + case nn.MultiheadAttention(): + attn_output, _ = self.mhsa( + x, + x, + x, + need_weights=False, + attn_mask=( + attn_mask.repeat(self.mhsa.num_heads, 1, 1) + if attn_mask is not None + else None + ), + ) + case MultiHeadALiBi(): + attn_output = self.mhsa( + q=x, + k=x, + v=x, + coords_q=coords, + coords_k=coords, + attn_mask=attn_mask, + alibi_mask=alibi_mask, + ) + case _ as unreachable: + assert_never(unreachable) + + return attn_output + + +class Transformer(nn.Module): + def __init__( + self, + *, + dim: int, + depth: int, + heads: int, + mlp_dim: int, + dropout: float, + use_alibi: bool, + ) -> None: + super().__init__() + self.depth = depth + self.layers = nn.ModuleList( + [ + nn.ModuleList( + [ + SelfAttention( + dim=dim, + num_heads=heads, + dropout=dropout, + use_alibi=use_alibi, + ), + feed_forward( + dim, + mlp_dim, + ), + ] + ) + for _ in range(depth) + ] + ) + + self.norm = nn.LayerNorm(dim) + + @jaxtyped(typechecker=beartype) + def forward( + self, + x: Float[Tensor, "batch sequence proj_feature"], + *, + coords: Float[Tensor, "batch sequence 2"], + attn_mask: Bool[Tensor, "batch sequence sequence"] | None, + alibi_mask: Bool[Tensor, "batch sequence sequence"] | None, + ) -> Float[Tensor, "batch sequence proj_feature"]: + for attn, ff in cast(Iterable[tuple[nn.Module, nn.Module]], self.layers): + x_attn = attn(x, coords=coords, attn_mask=attn_mask, alibi_mask=alibi_mask) + x = x_attn + x + x = ff(x) + x + + x = self.norm(x) + return x + + +class VisionTransformer(nn.Module): + def __init__( + self, + *, + dim_output: int, + dim_input: int, + dim_model: int, + n_layers: int, + n_heads: int, + dim_feedforward: int, + dropout: float, + use_alibi: bool, + ) -> None: + super().__init__() + self.class_token = nn.Parameter(torch.randn(dim_model)) + + self.project_features = nn.Sequential( + nn.Linear(dim_input, dim_model, bias=True), + nn.GELU(), + nn.Dropout(dropout), + ) + + self.transformer = Transformer( + dim=dim_model, + depth=n_layers, + heads=n_heads, + mlp_dim=dim_feedforward, + dropout=dropout, + use_alibi=use_alibi, + ) + + self.mlp_head = nn.Sequential(nn.Linear(dim_model, dim_output)) + + @jaxtyped(typechecker=beartype) + def forward( + self, + bags: Float[Tensor, "batch tile feature"], + *, + coords: Float[Tensor, "batch tile 2"], + mask: Bool[Tensor, "batch tile"] | None, + ) -> Float[Tensor, "batch logit"]: + batch_size, _n_tiles, _n_features = bags.shape + + # Map input sequence to latent space of TransMIL + bags = self.project_features(bags) + + # Prepend a class token to every bag, + # include it in the mask. + # TODO should the tiles be able to refer to the class token? Test! + cls_tokens = repeat(self.class_token, "d -> b 1 d", b=batch_size) + bags = torch.cat([cls_tokens, bags], dim=1) + coords = torch.cat( + [torch.zeros(batch_size, 1, 2).type_as(coords), coords], dim=1 + ) + + match mask: + case None: + bags = self.transformer( + bags, coords=coords, attn_mask=None, alibi_mask=None + ) + + case _: + mask_with_class_token = torch.cat( + [torch.zeros(mask.shape[0], 1).type_as(mask), mask], dim=1 + ) + square_attn_mask = torch.einsum( + "bq,bk->bqk", mask_with_class_token, mask_with_class_token + ) + # Don't allow other tiles to reference the class token + square_attn_mask[:, 1:, 0] = True + + # Don't apply ALiBi to the query, as the coordinates don't make sense here + alibi_mask = torch.zeros_like(square_attn_mask) + alibi_mask[:, 0, :] = True + alibi_mask[:, :, 0] = True + + bags = self.transformer( + bags, + coords=coords, + attn_mask=square_attn_mask, + alibi_mask=alibi_mask, + ) + + # Only take class token + bags = bags[:, 0] + + return self.mlp_head(bags) From ebb3b10a954e35b76db10b69d1663939bced1d5a Mon Sep 17 00:00:00 2001 From: GitHub CI Date: Thu, 17 Jul 2025 15:15:20 +0200 Subject: [PATCH 3/9] feat: add configuration files for federated client and server setup Signed-off-by: GitHub CI --- .../stamp/app/config/config_fed_client.conf | 135 ++++++++++++++++++ .../stamp/app/config/config_fed_server.conf | 27 ++++ 2 files changed, 162 insertions(+) create mode 100644 application/jobs/stamp/app/config/config_fed_client.conf create mode 100644 application/jobs/stamp/app/config/config_fed_server.conf diff --git a/application/jobs/stamp/app/config/config_fed_client.conf b/application/jobs/stamp/app/config/config_fed_client.conf new file mode 100644 index 00000000..b15e14cb --- /dev/null +++ b/application/jobs/stamp/app/config/config_fed_client.conf @@ -0,0 +1,135 @@ +{ + format_version = 2 + app_script = "main.py" + app_config = "" + executors = [ + { + tasks = [ + "train" + ] + executor { + path = "nvflare.app_opt.pt.client_api_launcher_executor.PTClientAPILauncherExecutor" + args { + launcher_id = "launcher" + pipe_id = "pipe" + last_result_transfer_timeout = 3600 + external_pre_init_timeout = 3600 + peer_read_timeout = 3600 + heartbeat_timeout = 3600 + params_exchange_format = "pytorch" + params_transfer_type = "DIFF" + train_with_evaluation = true + } + } + } + { + # All tasks prefixed with swarm_ are routed to SwarmClientController + tasks = ["swarm_*"] + executor { + # client-side controller for training and logic and aggregation management + path = "controller.SwarmClientController" + args { + # train task must be implemented by Executor + learn_task_name = "train" + # how long to wait for current learn task before timing out the gathering + learn_task_timeout = 3600 + learn_task_abort_timeout = 3600 + learn_task_ack_timeout = 3600 + final_result_ack_timeout = 3600 + + # ids must map to corresponding components + persistor_id = "persistor" + aggregator_id = "aggregator" + shareable_generator_id = "shareable_generator" + min_responses_required = 2 + wait_time_after_min_resps_received = 3600 + } + } + } + ] + task_data_filters = [] + task_result_filters = [] + components = [ + { + id = "launcher" + path = "nvflare.app_common.launchers.subprocess_launcher.SubprocessLauncher" + args { + script = "python3 custom/{app_script} {app_config} " + launch_once = true + } + } + { + id = "aggregator" + path = "nvflare.app_common.aggregators.intime_accumulate_model_aggregator.InTimeAccumulateWeightedAggregator" + args { + expected_data_kind = "WEIGHT_DIFF" + } + } + { + id = "pipe" + path = "nvflare.fuel.utils.pipe.cell_pipe.CellPipe" + args { + mode = "PASSIVE" + site_name = "{SITE_NAME}" + token = "{JOB_ID}" + root_url = "{ROOT_URL}" + secure_mode = "{SECURE_MODE}" + workspace_dir = "{WORKSPACE}" + } + } + { + id = "persistor" + path = "nvflare.app_opt.pt.file_model_persistor.PTFileModelPersistor" + args { + model { + path = "modeling.lightning_model.LitVisionTransformer" + args { + + } + } + } + } + { + id = "shareable_generator" + path = "nvflare.app_common.ccwf.comps.simple_model_shareable_generator.SimpleModelShareableGenerator" + args {} + } + { + id = "metrics_pipe" + path = "nvflare.fuel.utils.pipe.cell_pipe.CellPipe" + args { + mode = "PASSIVE" + site_name = "{SITE_NAME}" + token = "{JOB_ID}" + root_url = "{ROOT_URL}" + secure_mode = "{SECURE_MODE}" + workspace_dir = "{WORKSPACE}" + } + } + { + id = "metric_relay" + path = "nvflare.app_common.widgets.metric_relay.MetricRelay" + args { + pipe_id = "metrics_pipe" + event_type = "fed.analytix_log_stats" + read_interval = 0.1 + } + } + { + id = "model_selector" + path = "nvflare.app_common.widgets.intime_model_selector.IntimeModelSelector" + args { + key_metric = "accuracy" + } + } + { + id = "config_preparer" + path = "nvflare.app_common.widgets.external_configurator.ExternalConfigurator" + args { + component_ids = [ + "metric_relay" + ] + } + } + ] +} diff --git a/application/jobs/stamp/app/config/config_fed_server.conf b/application/jobs/stamp/app/config/config_fed_server.conf new file mode 100644 index 00000000..c8a11577 --- /dev/null +++ b/application/jobs/stamp/app/config/config_fed_server.conf @@ -0,0 +1,27 @@ +format_version = 2 +task_data_filters = [] +task_result_filters = [] +components = [ + { + # write validation results to json file + id = "json_generator" + path = "nvflare.app_common.widgets.validation_json_generator.ValidationJsonGenerator" + args {} + } +] +workflows = [ + { + # server-side controller to manage job life cycle + id = "swarm_controller" + path = "controller.SwarmServerController" + args { + # can also set aggregation clients and train clients, see class for all available args + num_rounds = 20 + start_task_timeout = 3600 + progress_timeout = 3600 + end_workflow_timeout = 3600 + configure_task_timeout = 3600 + max_status_report_interval = 3600 + } + } +] From 6e5c62378c3c4b0be4e315f7ed067b83b8cdf6ed Mon Sep 17 00:00:00 2001 From: GitHub CI Date: Mon, 21 Jul 2025 16:04:49 +0200 Subject: [PATCH 4/9] refactor: reorganize project structure and update import paths for STAMP integration Signed-off-by: GitHub CI --- .../app/config/config_fed_client.conf | 2 +- .../app/config/config_fed_server.conf | 3 +- .../app/config/config_fed_client.conf | 2 +- .../app/config/config_fed_server.conf | 4 +- .../app/custom/main.py | 14 +- .../stamp/app/config/config_fed_client.conf | 26 +- .../stamp/app/config/config_fed_server.conf | 11 +- .../app/custom/{modeling => }/__init__.py | 0 .../stamp/app/custom/{modeling => }/alibi.py | 0 .../stamp/app/custom/{modeling => }/config.py | 3 +- .../app/custom/{modeling => }/crossval.py | 13 +- .../stamp/app/custom/{modeling => }/data.py | 7 +- .../stamp/app/custom/{modeling => }/deploy.py | 3 +- application/jobs/stamp/app/custom/main.py | 381 +- .../app/custom/modeling/lightning_model.py | 116 +- .../jobs/stamp/app/custom/modeling/train.py | 319 -- .../app/custom/modeling/vision_transformer.py | 2 +- .../app/custom/{modeling => }/transforms.py | 0 .../provision/project_Odelia_allsites.yml | 4 +- buildDockerImageAndStartupKits.sh | 4 - docker_config/Dockerfile_stamp | 77 +- docker_config/pyproject.toml | 142 + docker_config/uv.lock | 3641 +++++++++++++++++ 23 files changed, 4303 insertions(+), 471 deletions(-) rename application/jobs/stamp/app/custom/{modeling => }/__init__.py (100%) rename application/jobs/stamp/app/custom/{modeling => }/alibi.py (100%) rename application/jobs/stamp/app/custom/{modeling => }/config.py (97%) rename application/jobs/stamp/app/custom/{modeling => }/crossval.py (95%) rename application/jobs/stamp/app/custom/{modeling => }/data.py (99%) rename application/jobs/stamp/app/custom/{modeling => }/deploy.py (99%) delete mode 100644 application/jobs/stamp/app/custom/modeling/train.py rename application/jobs/stamp/app/custom/{modeling => }/transforms.py (100%) create mode 100644 docker_config/pyproject.toml create mode 100644 docker_config/uv.lock diff --git a/application/jobs/ODELIA_ternary_classification/app/config/config_fed_client.conf b/application/jobs/ODELIA_ternary_classification/app/config/config_fed_client.conf index 42ac6ebb..3b82afec 100644 --- a/application/jobs/ODELIA_ternary_classification/app/config/config_fed_client.conf +++ b/application/jobs/ODELIA_ternary_classification/app/config/config_fed_client.conf @@ -27,7 +27,7 @@ tasks = ["swarm_*"] executor { # client-side controller for training and logic and aggregation management - path = "controller.SwarmClientController" + path = "nvflare.app_common.ccwf.SwarmClientController" args { # train task must be implemented by Executor learn_task_name = "train" diff --git a/application/jobs/ODELIA_ternary_classification/app/config/config_fed_server.conf b/application/jobs/ODELIA_ternary_classification/app/config/config_fed_server.conf index fe11655d..335b173e 100644 --- a/application/jobs/ODELIA_ternary_classification/app/config/config_fed_server.conf +++ b/application/jobs/ODELIA_ternary_classification/app/config/config_fed_server.conf @@ -13,13 +13,12 @@ workflows = [ { # server-side controller to manage job life cycle id = "swarm_controller" - path = "controller.SwarmServerController" + path = "nvflare.app_common.ccwf.SwarmServerController" args { # can also set aggregation clients and train clients, see class for all available args num_rounds = 20 start_task_timeout = 360000 progress_timeout = 360000 - end_workflow_timeout = 360000 configure_task_timeout = 360000 max_status_report_interval = 360000 } diff --git a/application/jobs/minimal_training_pytorch_cnn/app/config/config_fed_client.conf b/application/jobs/minimal_training_pytorch_cnn/app/config/config_fed_client.conf index ff18ef95..92c80ae5 100644 --- a/application/jobs/minimal_training_pytorch_cnn/app/config/config_fed_client.conf +++ b/application/jobs/minimal_training_pytorch_cnn/app/config/config_fed_client.conf @@ -27,7 +27,7 @@ tasks = ["swarm_*"] executor { # client-side controller for training and logic and aggregation management - path = "controller.SwarmClientController" + path = "nvflare.app_common.ccwf.SwarmClientController" args { # train task must be implemented by Executor learn_task_name = "train" diff --git a/application/jobs/minimal_training_pytorch_cnn/app/config/config_fed_server.conf b/application/jobs/minimal_training_pytorch_cnn/app/config/config_fed_server.conf index 0ece834f..309d239f 100644 --- a/application/jobs/minimal_training_pytorch_cnn/app/config/config_fed_server.conf +++ b/application/jobs/minimal_training_pytorch_cnn/app/config/config_fed_server.conf @@ -13,13 +13,13 @@ workflows = [ { # server-side controller to manage job life cycle id = "swarm_controller" - path = "controller.SwarmServerController" + path = "nvflare.app_common.ccwf.SwarmServerController" args { # can also set aggregation clients and train clients, see class for all available args num_rounds = 5 start_task_timeout = 36000 progress_timeout = 36000 - end_workflow_timeout = 36000 + #end_workflow_timeout = 36000 configure_task_timeout = 36000 max_status_report_interval = 36000 } diff --git a/application/jobs/minimal_training_pytorch_cnn/app/custom/main.py b/application/jobs/minimal_training_pytorch_cnn/app/custom/main.py index a8ac2752..340f3b97 100755 --- a/application/jobs/minimal_training_pytorch_cnn/app/custom/main.py +++ b/application/jobs/minimal_training_pytorch_cnn/app/custom/main.py @@ -2,14 +2,14 @@ import os -import nvflare.client.lightning as flare import nvflare.client as flare_util +import nvflare.client.lightning as flare import torch import minimal_training TRAINING_MODE = os.getenv("TRAINING_MODE") - +TRAINING_MODE = "swarm" if TRAINING_MODE == "swarm": flare_util.init() SITE_NAME=flare.get_site_name() @@ -34,7 +34,17 @@ def main(): logger.info(f"Site name: {SITE_NAME}") while flare.is_running(): + logger.info('Waiting for input model from server...') input_model = flare.receive() + + if input_model is not None: + logger.info("==== Swarm model received ====") + logger.info( + f"input_model.params.keys() = {list(input_model.params.keys())[:10]} ... total = {len(input_model.params)}") + logger.info( + f"model.state_dict().keys() = {list(model.state_dict().keys())[:10]} ... total = {len(model.state_dict())}") + + logger.info(f"Current round: {input_model.current_round}") minimal_training.validate_and_train(logger, data_module, model, trainer) diff --git a/application/jobs/stamp/app/config/config_fed_client.conf b/application/jobs/stamp/app/config/config_fed_client.conf index b15e14cb..5565b668 100644 --- a/application/jobs/stamp/app/config/config_fed_client.conf +++ b/application/jobs/stamp/app/config/config_fed_client.conf @@ -12,10 +12,10 @@ args { launcher_id = "launcher" pipe_id = "pipe" - last_result_transfer_timeout = 3600 - external_pre_init_timeout = 3600 - peer_read_timeout = 3600 - heartbeat_timeout = 3600 + last_result_transfer_timeout = 360000 + external_pre_init_timeout = 360000 + peer_read_timeout = 360000 + heartbeat_timeout = 360000 params_exchange_format = "pytorch" params_transfer_type = "DIFF" train_with_evaluation = true @@ -27,22 +27,22 @@ tasks = ["swarm_*"] executor { # client-side controller for training and logic and aggregation management - path = "controller.SwarmClientController" + path = "nvflare.app_common.ccwf.SwarmClientController" args { # train task must be implemented by Executor learn_task_name = "train" # how long to wait for current learn task before timing out the gathering - learn_task_timeout = 3600 - learn_task_abort_timeout = 3600 - learn_task_ack_timeout = 3600 - final_result_ack_timeout = 3600 + #learn_task_timeout = 360000 + #learn_task_abort_timeout = 360000 + #learn_task_ack_timeout = 360000 + #final_result_ack_timeout = 360000 # ids must map to corresponding components persistor_id = "persistor" aggregator_id = "aggregator" shareable_generator_id = "shareable_generator" min_responses_required = 2 - wait_time_after_min_resps_received = 3600 + #wait_time_after_min_resps_received = 360000 } } } @@ -84,7 +84,11 @@ model { path = "modeling.lightning_model.LitVisionTransformer" args { - + dim_model = 512 + dim_feedforward=2048 + n_heads=8 + n_layers=2 + dropout=0.25 } } } diff --git a/application/jobs/stamp/app/config/config_fed_server.conf b/application/jobs/stamp/app/config/config_fed_server.conf index c8a11577..b013ea37 100644 --- a/application/jobs/stamp/app/config/config_fed_server.conf +++ b/application/jobs/stamp/app/config/config_fed_server.conf @@ -13,15 +13,14 @@ workflows = [ { # server-side controller to manage job life cycle id = "swarm_controller" - path = "controller.SwarmServerController" + path = "nvflare.app_common.ccwf.SwarmServerController" args { # can also set aggregation clients and train clients, see class for all available args num_rounds = 20 - start_task_timeout = 3600 - progress_timeout = 3600 - end_workflow_timeout = 3600 - configure_task_timeout = 3600 - max_status_report_interval = 3600 + #start_task_timeout = 360000 + #progress_timeout = 360000 + #configure_task_timeout = 360000 + #max_status_report_interval = 360000 } } ] diff --git a/application/jobs/stamp/app/custom/modeling/__init__.py b/application/jobs/stamp/app/custom/__init__.py similarity index 100% rename from application/jobs/stamp/app/custom/modeling/__init__.py rename to application/jobs/stamp/app/custom/__init__.py diff --git a/application/jobs/stamp/app/custom/modeling/alibi.py b/application/jobs/stamp/app/custom/alibi.py similarity index 100% rename from application/jobs/stamp/app/custom/modeling/alibi.py rename to application/jobs/stamp/app/custom/alibi.py diff --git a/application/jobs/stamp/app/custom/modeling/config.py b/application/jobs/stamp/app/custom/config.py similarity index 97% rename from application/jobs/stamp/app/custom/modeling/config.py rename to application/jobs/stamp/app/custom/config.py index 74f7e429..09fb4086 100644 --- a/application/jobs/stamp/app/custom/modeling/config.py +++ b/application/jobs/stamp/app/custom/config.py @@ -3,7 +3,8 @@ import torch from pydantic import BaseModel, ConfigDict, Field -from stamp.types import PandasLabel + +from modeling.types import PandasLabel class TrainConfig(BaseModel): diff --git a/application/jobs/stamp/app/custom/modeling/crossval.py b/application/jobs/stamp/app/custom/crossval.py similarity index 95% rename from application/jobs/stamp/app/custom/modeling/crossval.py rename to application/jobs/stamp/app/custom/crossval.py index 4ff21ce7..8f5148a9 100644 --- a/application/jobs/stamp/app/custom/modeling/crossval.py +++ b/application/jobs/stamp/app/custom/crossval.py @@ -7,23 +7,24 @@ from lightning.pytorch.accelerators.accelerator import Accelerator from pydantic import BaseModel from sklearn.model_selection import StratifiedKFold -from stamp.modeling.data import ( + +from data import ( PatientData, filter_complete_patient_data_, patient_to_ground_truth_from_clini_table_, slide_to_patient_from_slide_table_, ) -from stamp.modeling.deploy import _predict, _to_prediction_df -from stamp.modeling.lightning_model import LitVisionTransformer -from stamp.modeling.train import setup_model_for_training, train_model_ -from stamp.modeling.transforms import VaryPrecisionTransform -from stamp.types import ( +from deploy import _predict, _to_prediction_df +from main import setup_model_for_training, train_model_ +from modeling.lightning_model import LitVisionTransformer +from modeling.types import ( Category, FeaturePath, GroundTruth, PandasLabel, PatientId, ) +from transforms import VaryPrecisionTransform __author__ = "Marko van Treeck" __copyright__ = "Copyright (C) 2024 Marko van Treeck" diff --git a/application/jobs/stamp/app/custom/modeling/data.py b/application/jobs/stamp/app/custom/data.py similarity index 99% rename from application/jobs/stamp/app/custom/modeling/data.py rename to application/jobs/stamp/app/custom/data.py index 10b157f4..f8d4482b 100755 --- a/application/jobs/stamp/app/custom/modeling/data.py +++ b/application/jobs/stamp/app/custom/data.py @@ -14,7 +14,10 @@ import torch from jaxtyping import Bool, Float from packaging.version import Version -from stamp.types import ( +from torch import Tensor +from torch.utils.data import DataLoader, Dataset + +from modeling.types import ( Bags, BagSize, BagSizes, @@ -30,8 +33,6 @@ SlideMPP, TilePixels, ) -from torch import Tensor -from torch.utils.data import DataLoader, Dataset _logger = logging.getLogger("stamp") diff --git a/application/jobs/stamp/app/custom/modeling/deploy.py b/application/jobs/stamp/app/custom/deploy.py similarity index 99% rename from application/jobs/stamp/app/custom/modeling/deploy.py rename to application/jobs/stamp/app/custom/deploy.py index f0cda653..235f7f8a 100644 --- a/application/jobs/stamp/app/custom/modeling/deploy.py +++ b/application/jobs/stamp/app/custom/deploy.py @@ -17,7 +17,8 @@ slide_to_patient_from_slide_table_, ) from stamp.modeling.lightning_model import LitVisionTransformer -from stamp.types import GroundTruth, PandasLabel, PatientId + +from modeling.types import GroundTruth, PandasLabel, PatientId __all__ = ["deploy_categorical_model_"] diff --git a/application/jobs/stamp/app/custom/main.py b/application/jobs/stamp/app/custom/main.py index df9e75fe..cf85e048 100644 --- a/application/jobs/stamp/app/custom/main.py +++ b/application/jobs/stamp/app/custom/main.py @@ -1,19 +1,347 @@ import logging -import os import sys + +logger = logging.getLogger("stamp") +logger.setLevel(logging.DEBUG) + +if not logger.hasHandlers(): + handler = logging.StreamHandler(sys.stderr) + handler.setFormatter(logging.Formatter("%(asctime)s\t%(levelname)s\t%(message)s")) + handler.setLevel(logging.DEBUG) + logger.addHandler(handler) + +import shutil +from collections.abc import Callable, Mapping, Sequence from pathlib import Path +from typing import cast + +import lightning +import lightning.pytorch +import lightning.pytorch.accelerators +import lightning.pytorch.accelerators.accelerator +import torch +from lightning.pytorch.accelerators.accelerator import Accelerator +from lightning.pytorch.callbacks import ModelCheckpoint +from lightning.pytorch.loggers import CSVLogger +from sklearn.model_selection import train_test_split +from torch.utils.data.dataloader import DataLoader +import logging +import nvflare.client.lightning as flare +import nvflare.client as flare_util + +from data import ( + BagDataset, + PatientData, + dataloader_from_patient_data, + filter_complete_patient_data_, + patient_to_ground_truth_from_clini_table_, + slide_to_patient_from_slide_table_, +) +from modeling.lightning_model import ( + Bags, + BagSizes, + EncodedTargets, + LitVisionTransformer, +) +from transforms import VaryPrecisionTransform +from modeling.types import Category, CoordinatesBatch, GroundTruth, PandasLabel, PatientId + + +def train_categorical_model_( + *, + clini_table: Path, + slide_table: Path, + feature_dir: Path, + output_dir: Path, + patient_label: PandasLabel, + ground_truth_label: PandasLabel, + filename_label: PandasLabel, + categories: Sequence[Category] | None, + # Dataset and -loader parameters + bag_size: int, + num_workers: int, + # Training paramenters + batch_size: int, + max_epochs: int, + patience: int, + accelerator: str | Accelerator, + # Experimental features + use_vary_precision_transform: bool, + use_alibi: bool, +) -> None: + """Trains a model. + + Args: + clini_table: + An excel or csv file to read the clinical information from. + Must at least have the columns specified in the arguments + + `patient_label` (containing a unique patient ID) + and `ground_truth_label` (containing the ground truth to train for). + slide_table: + An excel or csv file to read the patient-slide associations from. + Must at least have the columns specified in the arguments + `patient_label` (containing the patient ID) + and `filename_label` + (containing a filename relative to `feature_dir` + in which some of the patient's features are stored). + feature_dir: + See `slide_table`. + output_dir: + Path into which to output the artifacts (trained model etc.) + generated during training. + patient_label: + See `clini_table`, `slide_table`. + ground_truth_label: + See `clini_table`. + filename_label: + See `slide_table`. + categories: + Categories of the ground truth. + Set to `None` to automatically infer. + """ + # Read and parse data from out clini and slide table + patient_to_ground_truth = patient_to_ground_truth_from_clini_table_( + clini_table_path=clini_table, + ground_truth_label=ground_truth_label, + patient_label=patient_label, + ) + slide_to_patient = slide_to_patient_from_slide_table_( + slide_table_path=slide_table, + feature_dir=feature_dir, + patient_label=patient_label, + filename_label=filename_label, + ) + + # Clean data (remove slides without ground truth, missing features, etc.) + patient_to_data = filter_complete_patient_data_( + patient_to_ground_truth=patient_to_ground_truth, + slide_to_patient=slide_to_patient, + drop_patients_with_missing_ground_truth=True, + ) + + # Train the model + model, train_dl, valid_dl = setup_model_for_training( + patient_to_data=patient_to_data, + categories=categories, + bag_size=bag_size, + batch_size=batch_size, + num_workers=num_workers, + ground_truth_label=ground_truth_label, + clini_table=clini_table, + slide_table=slide_table, + feature_dir=feature_dir, + train_transform=( + VaryPrecisionTransform(min_fraction_bits=1) + if use_vary_precision_transform + else None + ), + use_alibi=use_alibi, + ) + train_model_( + output_dir=output_dir, + model=model, + train_dl=train_dl, + valid_dl=valid_dl, + max_epochs=max_epochs, + patience=patience, + accelerator=accelerator, + ) + + +def train_model_( + *, + output_dir: Path, + model: LitVisionTransformer, + train_dl: DataLoader[tuple[Bags, CoordinatesBatch, BagSizes, EncodedTargets]], + valid_dl: DataLoader[tuple[Bags, CoordinatesBatch, BagSizes, EncodedTargets]], + max_epochs: int, + patience: int, + accelerator: str | Accelerator, +) -> LitVisionTransformer: + """Trains a model. + + Returns: + The model with the best validation loss during training. + """ + torch.set_float32_matmul_precision("high") + + model_checkpoint = ModelCheckpoint( + monitor="validation_loss", + mode="min", + filename="checkpoint-{epoch:02d}-{validation_loss:0.3f}", + ) + trainer = lightning.Trainer( + default_root_dir=output_dir, + callbacks=[ + # EarlyStopping(monitor="validation_loss", mode="min", patience=patience), + model_checkpoint, + ], + + max_epochs=max_epochs, + # FIXME The number of accelerators is currently fixed to one for the + # following reasons: + # 1. `trainer.predict()` does not return any predictions if used with + # the default strategy no multiple GPUs + # 2. `barspoon.model.SafeMulticlassAUROC` breaks on multiple GPUs + accelerator=accelerator, + devices=1, + gradient_clip_val=0.5, + logger=CSVLogger(save_dir=output_dir), + log_every_n_steps=len(train_dl), + # num_sanity_val_steps=0, + ) + flare_util.init() + SITE_NAME = flare.get_site_name() + flare.patch(trainer) # Patch trainer to enable swarm learning + logger.info(f"Site name: {SITE_NAME}") + + logger.info(" About to enter flare.is_running loop") + + while flare.is_running(): + logger.info("[DEBUG] waiting to receive swarm model (10s timeout)") + input_model = flare.receive(timeout=10) -from modeling.train import train_categorical_model_ + if input_model is not None: + logger.info("==== Swarm model received ====") + logger.info( + f"input_model.params.keys() = {list(input_model.params.keys())[:10]} ... total = {len(input_model.params)}") + logger.info( + f"model.state_dict().keys() = {list(model.state_dict().keys())[:10]} ... total = {len(model.state_dict())}") + try: + model.load_state_dict(input_model.params) + except Exception as e: + logger.error("load_state_dict failed:", exc_info=True) + raise -# Set up the logger -_logger = logging.getLogger("stamp") -_logger.setLevel(logging.DEBUG) -_formatter = logging.Formatter("%(asctime)s\t%(levelname)s\t%(message)s") + logger.info(f"[DEBUG] Got input model: {input_model}") + if input_model is None: + logger.info("[DEBUG] no swarm_start received in 10s") + else: + model.load_state_dict(input_model.params) -_stream_handler = logging.StreamHandler(sys.stderr) -_stream_handler.setLevel(logging.INFO) -_stream_handler.setFormatter(_formatter) -_logger.addHandler(_stream_handler) + logger.info("[DEBUG] received swarm_start:", input_model) + logger.info(f"Current round: {input_model.current_round}") + + trainer.fit( + model=model, + train_dataloaders=train_dl, + val_dataloaders=valid_dl, + ) + shutil.copy(model_checkpoint.best_model_path, output_dir / "model.ckpt") + + return LitVisionTransformer.load_from_checkpoint(model_checkpoint.best_model_path) + + +def setup_model_for_training( + *, + patient_to_data: Mapping[PatientId, PatientData[GroundTruth]], + categories: Sequence[Category] | None, + bag_size: int, + batch_size: int, + num_workers: int, + train_transform: Callable[[torch.Tensor], torch.Tensor] | None, + use_alibi: bool, + # Metadata, has no effect on model training + ground_truth_label: PandasLabel, + clini_table: Path, + slide_table: Path, + feature_dir: Path, +) -> tuple[ + LitVisionTransformer, + DataLoader[tuple[Bags, CoordinatesBatch, BagSizes, EncodedTargets]], + DataLoader[tuple[Bags, CoordinatesBatch, BagSizes, EncodedTargets]], +]: + """Creates a model and dataloaders for training""" + + # Do a stratified train-validation split + ground_truths = [ + patient_data.ground_truth + for patient_data in patient_to_data.values() + if patient_data.ground_truth is not None + ] + + if len(ground_truths) != len(patient_to_data): + raise ValueError( + "patient_to_data must have a ground truth defined for all targets!" + ) + + train_patients, valid_patients = cast( + tuple[Sequence[PatientId], Sequence[PatientId]], + train_test_split( + list(patient_to_data), stratify=ground_truths, shuffle=True, random_state=0 + ), + ) + + train_dl, train_categories = dataloader_from_patient_data( + patient_data=[patient_to_data[patient] for patient in train_patients], + categories=categories, + bag_size=bag_size, + batch_size=batch_size, + shuffle=True, + num_workers=num_workers, + transform=train_transform, + ) + del categories # Let's not accidentally reuse the original categories + valid_dl, _ = dataloader_from_patient_data( + patient_data=[patient_to_data[patient] for patient in valid_patients], + bag_size=None, # Use all the patient data for validation + categories=train_categories, + batch_size=1, + shuffle=False, + num_workers=num_workers, + transform=None, + ) + if overlap := set(train_patients) & set(valid_patients): + raise RuntimeError( + f"unreachable: unexpected overlap between training and validation set: {overlap}" + ) + + # Sample one bag to infer the input dimensions of the model + bags, coords, bag_sizes, targets = cast( + tuple[Bags, CoordinatesBatch, BagSizes, EncodedTargets], next(iter(train_dl)) + ) + _, _, dim_feats = bags.shape + + # Weigh classes inversely to their occurrence + category_counts = cast(BagDataset, train_dl.dataset).ground_truths.sum(dim=0) + cat_ratio_reciprocal = category_counts.sum() / category_counts + category_weights = cat_ratio_reciprocal / cat_ratio_reciprocal.sum() + + if len(train_categories) <= 1: + raise ValueError(f"not enough categories to train on: {train_categories}") + elif any(category_counts < 16): + underpopulated_categories = { + category: count + for category, count in zip(train_categories, category_counts, strict=True) + if count < 16 + } + _logger.warning( + f"Some categories do not have enough samples to meaningfully train a model: {underpopulated_categories}. " + "You may want to consider removing these categories; the model will likely overfit on the few samples available." + ) + + # Train the model + + model = LitVisionTransformer(dim_model=512, + dim_feedforward=2048, + n_heads=8, + n_layers=2, + dropout=0.25) + model.set( + categories=train_categories, + category_weights=category_weights, + dim_input=dim_feats, + use_alibi=use_alibi, + ground_truth_label=ground_truth_label, + train_patients=train_patients, + valid_patients=valid_patients, + clini_table=clini_table, + slide_table=slide_table, + feature_dir=feature_dir, + ) + + return model, train_dl, valid_dl def _add_file_handle_(logger: logging.Logger, *, output_dir: Path) -> None: @@ -29,9 +357,11 @@ def _add_file_handle_(logger: logging.Logger, *, output_dir: Path) -> None: def main(): + '''' output_dir = os.getenv("TRAINING_OUTPUT_DIR") - _add_file_handle_(_logger, output_dir=Path(output_dir)) - _logger.info("Using training configuration from environment variables.") + print('output_dir:', output_dir) + #_add_file_handle_(_logger, output_dir=Path(output_dir)) + #_logger.info("Using training configuration from environment variables.") train_categorical_model_( output_dir=Path(output_dir), @@ -54,7 +384,32 @@ def main(): use_vary_precision_transform=os.getenv("USE_VARY_PRECISION_TRANSFORM", "False").lower() == "true", use_alibi=os.getenv("USE_ALIBI", "False").lower() == "true", ) +''' + output_dir = "/mnt/swarm_alpha/ECDP2025/pathology_data/TCGA/TCGA-CRC/STAMP_crossval_new" + logger.info('output_dir:', output_dir) + # _add_file_handle_(_logger, output_dir=Path(output_dir)) + #_logger.info("Using training configuration from environment variables.") - + train_categorical_model_( + output_dir=Path(output_dir), + clini_table=Path("/mnt/swarm_alpha/ECDP2025/pathology_data/TCGA/TCGA-CRC/TCGA-CRC-DX_clini.xlsx"), + slide_table=Path("/mnt/swarm_alpha/ECDP2025/pathology_data/TCGA/TCGA-CRC/TCGA-CRC-DX_slide_h5.csv"), + feature_dir=Path("/mnt/swarm_alpha/ECDP2025/pathology_data/TCGA/TCGA-CRC/uni2-02627079"), + patient_label="PATIENT", + ground_truth_label="isMSIH", + filename_label="FILENAME", + categories=None, + # Dataset and loader parameters + bag_size=512, + num_workers=15, + # Training parameters + batch_size=64, + max_epochs=1, # 64 + patience=16, + accelerator="gpu", + # Experimental features + use_vary_precision_transform='true', + use_alibi='false', + ) if __name__ == "__main__": main() diff --git a/application/jobs/stamp/app/custom/modeling/lightning_model.py b/application/jobs/stamp/app/custom/modeling/lightning_model.py index 5a98018b..97089551 100644 --- a/application/jobs/stamp/app/custom/modeling/lightning_model.py +++ b/application/jobs/stamp/app/custom/modeling/lightning_model.py @@ -10,7 +10,6 @@ from torch import Tensor, nn, optim from torchmetrics.classification import MulticlassAUROC -from vision_transformer import VisionTransformer from .types import ( Bags, BagSizes, @@ -20,6 +19,7 @@ PandasLabel, PatientId, ) +from .vision_transformer import VisionTransformer Loss: TypeAlias = Float[Tensor, ""] @@ -58,79 +58,85 @@ class LitVisionTransformer(lightning.LightningModule): def __init__( self, *, - categories: Sequence[Category], - category_weights: Float[Tensor, "category_weight"], # noqa: F821 - dim_input: int, dim_model: int, dim_feedforward: int, n_heads: int, n_layers: int, dropout: float, - # Experimental features - # TODO remove default values for stamp 3; they're only here for backwards compatibility - use_alibi: bool = False, - # Metadata used by other parts of stamp, but not by the model itself - ground_truth_label: PandasLabel, - train_patients: Iterable[PatientId], - valid_patients: Iterable[PatientId], - # stamp_version: Version = Version(stamp.__version__), - # Other metadata **metadata, ) -> None: super().__init__() + self.dim_model = dim_model + self.dim_feedforward = dim_feedforward + self.n_heads = n_heads + self.n_layers = n_layers + self.dropout = dropout + self.metadata = metadata + + # Parameters to be set later + self.categories = None + self.category_weights = None + self.ground_truth_label = None + self.train_patients = None + self.valid_patients = None + self.use_alibi = False + self.dim_input = None + + # Optional external metadata + self.clini_table = None + self.slide_table = None + self.feature_dir = None + + self.vision_transformer = None + self.valid_auroc = None + + def set( + self, + *, + categories: Sequence[Category], + category_weights: Float[Tensor, "category_weight"], + dim_input: int, + use_alibi: bool = False, + ground_truth_label: PandasLabel, + train_patients: Iterable[PatientId], + valid_patients: Iterable[PatientId], + clini_table=None, + slide_table=None, + feature_dir=None, + ): if len(categories) != len(category_weights): - raise ValueError( - "the number of category weights has to match the number of categories!" - ) + raise ValueError("the number of category weights must match the number of categories!") + self.categories = np.array(categories) + self.category_weights = category_weights + self.dim_input = dim_input + self.use_alibi = use_alibi + self.ground_truth_label = ground_truth_label + self.train_patients = train_patients + self.valid_patients = valid_patients + + self.clini_table = clini_table + self.slide_table = slide_table + self.feature_dir = feature_dir + # Initialize model and AUROC after all info is available self.vision_transformer = VisionTransformer( dim_output=len(categories), dim_input=dim_input, - dim_model=dim_model, - n_layers=n_layers, - n_heads=n_heads, - dim_feedforward=dim_feedforward, - dropout=dropout, + dim_model=self.dim_model, + n_layers=self.n_layers, + n_heads=self.n_heads, + dim_feedforward=self.dim_feedforward, + dropout=self.dropout, use_alibi=use_alibi, ) self.class_weights = category_weights self.valid_auroc = MulticlassAUROC(len(categories)) + self.save_hyperparameters(ignore=["vision_transformer", "valid_auroc"]) - # Used during deployment - self.ground_truth_label = ground_truth_label - self.categories = np.array(categories) - self.train_patients = train_patients - self.valid_patients = valid_patients - - _ = metadata # unused, but saved in model - - # Check if version is compatible. - # This should only happen when the model is loaded, - # otherwise the default value will make these checks pass. - ''' - if stamp_version < Version("2.0.0.dev8"): - # Update this as we change our model in incompatible ways! - raise ValueError( - f"model has been built with stamp version {stamp_version} " - f"which is incompatible with the current version." - ) - - elif stamp_version > Version(stamp.__version__): - # Let's be strict with models "from the future", - # better fail deadly than have broken results. - raise ValueError( - "model has been built with a stamp version newer than the installed one " - f"({stamp_version} > {stamp.__version__}). " - "Please upgrade stamp to a compatible version." - ) - ''' - self.save_hyperparameters() - - def forward( - self, - bags: Bags, - ) -> Float[Tensor, "batch logit"]: + def forward(self, bags: Bags) -> Float[Tensor, "batch logit"]: + if self.vision_transformer is None: + raise RuntimeError("Model not initialized. Call `.set()` first.") return self.vision_transformer(bags) def _step( diff --git a/application/jobs/stamp/app/custom/modeling/train.py b/application/jobs/stamp/app/custom/modeling/train.py deleted file mode 100644 index 0d71f0ca..00000000 --- a/application/jobs/stamp/app/custom/modeling/train.py +++ /dev/null @@ -1,319 +0,0 @@ -import logging -import shutil -from collections.abc import Callable, Mapping, Sequence -from pathlib import Path -from typing import cast - -import lightning -import lightning.pytorch -import lightning.pytorch.accelerators -import lightning.pytorch.accelerators.accelerator -import torch -from lightning.pytorch.accelerators.accelerator import Accelerator -from lightning.pytorch.callbacks import EarlyStopping, ModelCheckpoint -from lightning.pytorch.loggers import CSVLogger -from sklearn.model_selection import train_test_split -from torch.utils.data.dataloader import DataLoader - -logging.basicConfig(level=logging.INFO) -logger = logging.getLogger(__name__) - -import nvflare.client.lightning as flare -import nvflare.client as flare_util - -flare_util.init() -SITE_NAME = flare.get_site_name() - -from data import ( - BagDataset, - PatientData, - dataloader_from_patient_data, - filter_complete_patient_data_, - patient_to_ground_truth_from_clini_table_, - slide_to_patient_from_slide_table_, -) -from lightning_model import ( - Bags, - BagSizes, - EncodedTargets, - LitVisionTransformer, -) -from stamp.modeling.transforms import VaryPrecisionTransform -from stamp.types import Category, CoordinatesBatch, GroundTruth, PandasLabel, PatientId - -__author__ = "Marko van Treeck" -__copyright__ = "Copyright (C) 2024 Marko van Treeck" -__license__ = "MIT" - -_logger = logging.getLogger("stamp") - - -def train_categorical_model_( - *, - clini_table: Path, - slide_table: Path, - feature_dir: Path, - output_dir: Path, - patient_label: PandasLabel, - ground_truth_label: PandasLabel, - filename_label: PandasLabel, - categories: Sequence[Category] | None, - # Dataset and -loader parameters - bag_size: int, - num_workers: int, - # Training paramenters - batch_size: int, - max_epochs: int, - patience: int, - accelerator: str | Accelerator, - # Experimental features - use_vary_precision_transform: bool, - use_alibi: bool, -) -> None: - """Trains a model. - - Args: - clini_table: - An excel or csv file to read the clinical information from. - Must at least have the columns specified in the arguments - - `patient_label` (containing a unique patient ID) - and `ground_truth_label` (containing the ground truth to train for). - slide_table: - An excel or csv file to read the patient-slide associations from. - Must at least have the columns specified in the arguments - `patient_label` (containing the patient ID) - and `filename_label` - (containing a filename relative to `feature_dir` - in which some of the patient's features are stored). - feature_dir: - See `slide_table`. - output_dir: - Path into which to output the artifacts (trained model etc.) - generated during training. - patient_label: - See `clini_table`, `slide_table`. - ground_truth_label: - See `clini_table`. - filename_label: - See `slide_table`. - categories: - Categories of the ground truth. - Set to `None` to automatically infer. - """ - # Read and parse data from out clini and slide table - patient_to_ground_truth = patient_to_ground_truth_from_clini_table_( - clini_table_path=clini_table, - ground_truth_label=ground_truth_label, - patient_label=patient_label, - ) - slide_to_patient = slide_to_patient_from_slide_table_( - slide_table_path=slide_table, - feature_dir=feature_dir, - patient_label=patient_label, - filename_label=filename_label, - ) - - # Clean data (remove slides without ground truth, missing features, etc.) - patient_to_data = filter_complete_patient_data_( - patient_to_ground_truth=patient_to_ground_truth, - slide_to_patient=slide_to_patient, - drop_patients_with_missing_ground_truth=True, - ) - - # Train the model - model, train_dl, valid_dl = setup_model_for_training( - patient_to_data=patient_to_data, - categories=categories, - bag_size=bag_size, - batch_size=batch_size, - num_workers=num_workers, - ground_truth_label=ground_truth_label, - clini_table=clini_table, - slide_table=slide_table, - feature_dir=feature_dir, - train_transform=( - VaryPrecisionTransform(min_fraction_bits=1) - if use_vary_precision_transform - else None - ), - use_alibi=use_alibi, - ) - train_model_( - output_dir=output_dir, - model=model, - train_dl=train_dl, - valid_dl=valid_dl, - max_epochs=max_epochs, - patience=patience, - accelerator=accelerator, - ) - - -def train_model_( - *, - output_dir: Path, - model: LitVisionTransformer, - train_dl: DataLoader[tuple[Bags, CoordinatesBatch, BagSizes, EncodedTargets]], - valid_dl: DataLoader[tuple[Bags, CoordinatesBatch, BagSizes, EncodedTargets]], - max_epochs: int, - patience: int, - accelerator: str | Accelerator, -) -> LitVisionTransformer: - """Trains a model. - - Returns: - The model with the best validation loss during training. - """ - torch.set_float32_matmul_precision("high") - - model_checkpoint = ModelCheckpoint( - monitor="validation_loss", - mode="min", - filename="checkpoint-{epoch:02d}-{validation_loss:0.3f}", - ) - trainer = lightning.Trainer( - default_root_dir=output_dir, - callbacks=[ - EarlyStopping(monitor="validation_loss", mode="min", patience=patience), - model_checkpoint, - ], - - max_epochs=max_epochs, - # FIXME The number of accelerators is currently fixed to one for the - # following reasons: - # 1. `trainer.predict()` does not return any predictions if used with - # the default strategy no multiple GPUs - # 2. `barspoon.model.SafeMulticlassAUROC` breaks on multiple GPUs - accelerator=accelerator, - devices=1, - gradient_clip_val=0.5, - logger=CSVLogger(save_dir=output_dir), - log_every_n_steps=len(train_dl), - ) - - flare.patch(trainer) # Patch trainer to enable swarm learning - logger.info(f"Site name: {SITE_NAME}") - - while flare.is_running(): - input_model = flare.receive() - logger.info(f"Current round: {input_model.current_round}") - trainer.fit( - model=model, - train_dataloaders=train_dl, - val_dataloaders=valid_dl, - ) - shutil.copy(model_checkpoint.best_model_path, output_dir / "model.ckpt") - - return LitVisionTransformer.load_from_checkpoint(model_checkpoint.best_model_path) - - -def setup_model_for_training( - *, - patient_to_data: Mapping[PatientId, PatientData[GroundTruth]], - categories: Sequence[Category] | None, - bag_size: int, - batch_size: int, - num_workers: int, - train_transform: Callable[[torch.Tensor], torch.Tensor] | None, - use_alibi: bool, - # Metadata, has no effect on model training - ground_truth_label: PandasLabel, - clini_table: Path, - slide_table: Path, - feature_dir: Path, -) -> tuple[ - LitVisionTransformer, - DataLoader[tuple[Bags, CoordinatesBatch, BagSizes, EncodedTargets]], - DataLoader[tuple[Bags, CoordinatesBatch, BagSizes, EncodedTargets]], -]: - """Creates a model and dataloaders for training""" - - # Do a stratified train-validation split - ground_truths = [ - patient_data.ground_truth - for patient_data in patient_to_data.values() - if patient_data.ground_truth is not None - ] - - if len(ground_truths) != len(patient_to_data): - raise ValueError( - "patient_to_data must have a ground truth defined for all targets!" - ) - - train_patients, valid_patients = cast( - tuple[Sequence[PatientId], Sequence[PatientId]], - train_test_split( - list(patient_to_data), stratify=ground_truths, shuffle=True, random_state=0 - ), - ) - - train_dl, train_categories = dataloader_from_patient_data( - patient_data=[patient_to_data[patient] for patient in train_patients], - categories=categories, - bag_size=bag_size, - batch_size=batch_size, - shuffle=True, - num_workers=num_workers, - transform=train_transform, - ) - del categories # Let's not accidentally reuse the original categories - valid_dl, _ = dataloader_from_patient_data( - patient_data=[patient_to_data[patient] for patient in valid_patients], - bag_size=None, # Use all the patient data for validation - categories=train_categories, - batch_size=1, - shuffle=False, - num_workers=num_workers, - transform=None, - ) - if overlap := set(train_patients) & set(valid_patients): - raise RuntimeError( - f"unreachable: unexpected overlap between training and validation set: {overlap}" - ) - - # Sample one bag to infer the input dimensions of the model - bags, coords, bag_sizes, targets = cast( - tuple[Bags, CoordinatesBatch, BagSizes, EncodedTargets], next(iter(train_dl)) - ) - _, _, dim_feats = bags.shape - - # Weigh classes inversely to their occurrence - category_counts = cast(BagDataset, train_dl.dataset).ground_truths.sum(dim=0) - cat_ratio_reciprocal = category_counts.sum() / category_counts - category_weights = cat_ratio_reciprocal / cat_ratio_reciprocal.sum() - - if len(train_categories) <= 1: - raise ValueError(f"not enough categories to train on: {train_categories}") - elif any(category_counts < 16): - underpopulated_categories = { - category: count - for category, count in zip(train_categories, category_counts, strict=True) - if count < 16 - } - _logger.warning( - f"Some categories do not have enough samples to meaningfully train a model: {underpopulated_categories}. " - "You may want to consider removing these categories; the model will likely overfit on the few samples available." - ) - - # Train the model - model = LitVisionTransformer( - categories=train_categories, - category_weights=category_weights, - dim_input=dim_feats, - dim_model=512, - dim_feedforward=2048, - n_heads=8, - n_layers=2, - dropout=0.25, - use_alibi=use_alibi, - # Metadata, has no effect on model training - ground_truth_label=ground_truth_label, - train_patients=train_patients, - valid_patients=valid_patients, - clini_table=clini_table, - slide_table=slide_table, - feature_dir=feature_dir, - ) - - return model, train_dl, valid_dl diff --git a/application/jobs/stamp/app/custom/modeling/vision_transformer.py b/application/jobs/stamp/app/custom/modeling/vision_transformer.py index a2d71c0b..3a710960 100755 --- a/application/jobs/stamp/app/custom/modeling/vision_transformer.py +++ b/application/jobs/stamp/app/custom/modeling/vision_transformer.py @@ -6,10 +6,10 @@ from typing import assert_never, cast import torch +from alibi import MultiHeadALiBi from beartype import beartype from einops import repeat from jaxtyping import Bool, Float, jaxtyped -from stamp.modeling.alibi import MultiHeadALiBi from torch import Tensor, nn diff --git a/application/jobs/stamp/app/custom/modeling/transforms.py b/application/jobs/stamp/app/custom/transforms.py similarity index 100% rename from application/jobs/stamp/app/custom/modeling/transforms.py rename to application/jobs/stamp/app/custom/transforms.py diff --git a/application/provision/project_Odelia_allsites.yml b/application/provision/project_Odelia_allsites.yml index 530b356d..8f1b1531 100644 --- a/application/provision/project_Odelia_allsites.yml +++ b/application/provision/project_Odelia_allsites.yml @@ -1,5 +1,5 @@ api_version: 3 -name: odelia___REPLACED_BY_CURRENT_VERSION_NUMBER_WHEN_BUILDING_STARTUP_KITS___allsites_test +name: odelia_1.0-dev.250718.ebb3b10_allsites_test description: Odelia TUD server all collaborators clients on Odelia challenge dataset provision http based yaml file participants: @@ -77,7 +77,7 @@ builders: # app_validator: PATH_TO_YOUR_OWN_APP_VALIDATOR # when docker_image is set to a docker image name, docker.sh will be generated on server/client/admin - docker_image: jefftud/odelia:__REPLACED_BY_CURRENT_VERSION_NUMBER_WHEN_BUILDING_STARTUP_KITS__ + docker_image: jefftud/odelia:1.0-dev.250718.ebb3b10 # download_job_url is set to http://download.server.com/ as default in fed_server.json. You can override this # to different url. diff --git a/buildDockerImageAndStartupKits.sh b/buildDockerImageAndStartupKits.sh index b55d9692..f87b2429 100755 --- a/buildDockerImageAndStartupKits.sh +++ b/buildDockerImageAndStartupKits.sh @@ -3,10 +3,6 @@ set -e # make sure we are building from a state without local changes -if ! git diff --quiet || ! git diff --staged --quiet ; then - echo "Local changes exist, aborting" - exit 1 -fi DOCKER_BUILD_ARGS="--no-cache --progress=plain" DOCKERFILE_SUFFIX="ODELIA" diff --git a/docker_config/Dockerfile_stamp b/docker_config/Dockerfile_stamp index 24e5734e..82dd9ed1 100644 --- a/docker_config/Dockerfile_stamp +++ b/docker_config/Dockerfile_stamp @@ -1,58 +1,53 @@ -# Base image with Python 3.11 (CUDA-enabled PyTorch image) -ARG PYTORCH_IMAGE=pytorch/pytorch:2.2.2-cuda12.1-cudnn8-runtime +# ----------- Base Image with Python 3.11 + CUDA 12.1 ----------- +ARG PYTORCH_IMAGE=pytorch/pytorch:2.2.2-cuda12.1-cudnn8-devel FROM ${PYTORCH_IMAGE} -# ------------------ Metadata ------------------ -ARG NVF_VERSION=2.4.1 -ENV NVF_BRANCH=${NVF_VERSION} -ENV PYTHON_VERSION=3.11 -ENV DEBIAN_FRONTEND=noninteractive -ENV PATH="/workspace/STAMP/.venv/bin:$PATH" -ENV HF_HOME=/workspace/.hf_cache +# ----------- Metadata & Environment Variables ----------- +ENV DEBIAN_FRONTEND=noninteractive \ + CUDA_HOME=/usr/local/cuda \ + HF_HOME=/workspace/.hf_cache \ + PATH="/workspace/STAMP/.venv/bin:$PATH" -# ------------------ System Dependencies ------------------ +# ----------- System Dependencies ----------- RUN apt-get update && apt-get install -y --no-install-recommends \ + python3.11 python3.11-venv python3.11-dev python3-pip \ git curl wget unzip ca-certificates build-essential \ - libglib2.0-dev libgl1 libglx-mesa0 libglib2.0-0 \ - openslide-tools libgl1-mesa-glx \ - python3.11 python3.11-venv python3.11-dev \ - && rm -rf /var/lib/apt/lists/* + openslide-tools libgl1 libglx-mesa0 libglib2.0-0 \ + && rm -rf /var/lib/apt/lists/* -# ------------------ Set Default Python ------------------ -RUN update-alternatives --install /usr/bin/python python /usr/bin/python3.11 1 && \ - update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.11 1 && \ - update-alternatives --install /usr/bin/pip pip /usr/bin/pip3 1 +# ----------- Install uv ----------- +RUN pip install uv -# ------------------ Install uv ------------------ -RUN curl -LsSf https://astral.sh/uv/install.sh | sh -s -- --yes - -# ------------------ NVFlare Installation ------------------ +# ----------- Clone STAMP and Install via uv.lock ----------- WORKDIR /workspace/ -COPY ./MediSwarm/docker_config/NVFlare /workspace/nvflare -COPY ./MediSwarm/docker_config/master_template.yml /workspace/nvflare/nvflare/lighter/impl/ -RUN python -m pip install /workspace/nvflare && rm -rf /workspace/nvflare - -# ------------------ MediSwarm Controller Installation ------------------ -COPY ./MediSwarm/controller /workspace/controller -RUN python -m pip install /workspace/controller && rm -rf /workspace/controller +RUN git clone https://github.com/KatherLab/STAMP.git +COPY docker_config/pyproject.toml docker_config/uv.lock /workspace/STAMP/ -# ------------------ STAMP Installation ------------------ -RUN git clone https://github.com/KatherLab/STAMP.git /workspace/STAMP WORKDIR /workspace/STAMP -RUN /root/.cargo/bin/uv venv && \ - /root/.cargo/bin/uv pip install --upgrade pip && \ - /root/.cargo/bin/uv sync --all-extras +RUN uv venv && uv pip install --upgrade pip && uv sync --all-extras -# ------------------ Expose stamp as CLI globally ------------------ +# ----------- Expose STAMP CLI globally ----------- RUN ln -s /workspace/STAMP/.venv/bin/stamp /usr/local/bin/stamp -# ------------------ HuggingFace CLI ------------------ -RUN /workspace/STAMP/.venv/bin/pip install huggingface_hub && \ - /workspace/STAMP/.venv/bin/huggingface-cli login --help || true +# ----------- HuggingFace CLI (Optional) ----------- +RUN huggingface-cli login --help || true + +# ----------- Install NVFlare ----------- +COPY ./docker_config/NVFlare /workspace/nvflare +COPY ./docker_config/master_template.yml /workspace/nvflare/nvflare/lighter/impl/ +RUN /usr/bin/python3.11 -m pip install /workspace/nvflare && rm -rf /workspace/nvflare -# ------------------ MediSwarm Source + Link ------------------ -COPY ./MediSwarm /MediSwarm +# ----------- Install MediSwarm Controller ----------- +COPY ./controller /workspace/controller +RUN /usr/bin/python3.11 -m pip install /workspace/controller && rm -rf /workspace/controller + +# ----------- Copy MediSwarm Source and Link ----------- +#COPY ./MediSwarm /MediSwarm originally +COPY ./ /MediSwarm RUN mkdir -p /fl_admin/transfer && ln -s /MediSwarm /fl_admin/transfer/MediSwarm -# ------------------ Default Workdir ------------------ +# ----------- Set Default Workdir ----------- WORKDIR /workspace/ + +#docker build -f docker_config/Dockerfile_stamp -t stamp-image . +#docker run --rm -it stamp-image:latest-image bash \ No newline at end of file diff --git a/docker_config/pyproject.toml b/docker_config/pyproject.toml new file mode 100644 index 00000000..e4992f44 --- /dev/null +++ b/docker_config/pyproject.toml @@ -0,0 +1,142 @@ +[project] +name = "stamp" +version = "2.2.0" +authors = [ + { name = "Omar El Nahhas", email = "omar.el_nahhas@tu-dresden.de" }, + { name = "Marko van Treeck", email = "markovantreeck@gmail.com" }, + { name = "Georg Wölflein", email = "georgw7777@gmail.com" }, + { name = "Tim Lenz", email = "tim.lenz@tu-dresden.de" }, + { name = "Laura Žigutytė", email = "laura.zigutyte@tu-dresden.de" }, + { name = "Cornelius Kummer", email = "cornelius.kummer@tu-dresden.de" }, + { name = "Juan Pablo Ricapito", email = "juan_pablo.ricapito@tu-dresden.de" } +] +description = "A protocol for Solid Tumor Associative Modeling in Pathology" +readme = "README.md" +requires-python = ">=3.11" + +classifiers = [ + "Programming Language :: Python :: 3", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", +] + +dependencies = [ + "beartype>=0.19.0", + "einops>=0.8.0", + "h5py>=3.12.1", + "jaxtyping>=0.2.36", + "lightning>=2.4.0", + "matplotlib>=3.9.2", + "numpy>=2.2.2", + "opencv-python>=4.10.0.84", + "openpyxl>=3.1.5", + "openslide-bin>=4.0.0.6", + "openslide-python>=1.4.1", + "packaging>=24.2", + "pandas>=2.2.3", + "pillow>=11.1.0", + "pydantic>=2.10.3", + "pyyaml>=6.0.2", + "scikit-learn>=1.5.2", + "scipy>=1.15.1", + "torch>=2.5.1", + "torchmetrics>=1.6.0", + "torchvision>=0.20.1", + "tqdm>=4.66.6", + "timm>=0.9.11", +] + +[project.optional-dependencies] +conch = [ + "huggingface-hub>=0.26.2", + "conch @ git+https://github.com/Mahmoodlab/CONCH.git@02d6ac59cc20874bff0f581de258c2b257f69a84", +] +conch1_5 = [ + "transformers>=4.45.2", + "einops-exts==0.0.4", +] +ctranspath = [ + "gdown>=5.2.0", +] +chief_ctranspath = [ + "gdown>=5.2.0", + "torch>=2.0.0" +] +gigapath = [ + "gigapath @ git+https://github.com/EzicStar/prov-gigapath.git@d4cf55321df37aaf867e24a31c61bcf490a296eb" +] +uni = [ + "huggingface-hub>=0.26.2", + "uni @ git+https://github.com/mahmoodlab/UNI.git", +] +virchow2 = [ + "huggingface-hub>=0.27.1", + "torch>=2.0.0", +] +cobra = [ + "jinja2>=3.1.4", + "cobra @ git+https://github.com/KatherLab/COBRA.git@f1a576e1133330ffc2d1df6ee110701921c7b7c9", +] +prism = [ + "sacremoses==0.1.1", + "environs==11.0.0", +] +madeleine = [ + "madeleine @ git+https://github.com/mahmoodlab/MADELEINE.git@de7c85acc2bdad352e6df8eee5694f8b6f288012" +] +musk = [ + "musk @ git+https://github.com/lilab-stanford/MUSK.git@e1699c27687f44bbf6d4adfcbb2abe89795d347f", +] +plip = [ + "transformers>=4.45.2" +] + +# Blanket target +all = ["stamp[conch,ctranspath,uni,virchow2,chief_ctranspath,conch1_5,prism,madeleine,musk,plip]"] + +[project.scripts] +"stamp" = "stamp.__main__:main" + +[project.urls] +"Homepage" = "https://github.com/KatherLab/STAMP" +"Bug Tracker" = "https://github.com/KatherLab/STAMP/issues" + +[dependency-groups] +dev = [ + "huggingface-hub>=0.27.1", + "ipykernel>=6.29.5", + "pyright>=1.1.389,!=1.1.391", + "pytest>=8.3.4", + "ruff>=0.8.1", +] + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[tool.hatch.metadata] +# To allow referencing git repos in dependencies +allow-direct-references = true + +[tool.pytest.ini_options] +markers = [ + "slow: marks tests as slow (deselect with '-m \"not slow\"')", +] + +[tool.ruff] +lint.ignore = ["F722"] # https://docs.kidger.site/jaxtyping/faq/#flake8-or-ruff-are-throwing-an-error + +[[tool.uv.dependency-metadata]] +name = "uni" +version = "v0.1.0" +requires-dist = [ + "torch>=2.0.1", + "torchvision", + "timm>=0.9.8", + "numpy", + "pandas", + "scikit-learn", + "tqdm", + "transformers", + "xformers; sys_platform != 'darwin'" # xformers is not supported on macOS +] \ No newline at end of file diff --git a/docker_config/uv.lock b/docker_config/uv.lock new file mode 100644 index 00000000..d29d37c7 --- /dev/null +++ b/docker_config/uv.lock @@ -0,0 +1,3641 @@ +version = 1 +requires-python = ">=3.11" +resolution-markers = [ + "python_version < '0'", + "python_full_version < '3.12' and sys_platform == 'darwin'", + "python_full_version < '3.12' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "python_full_version < '3.12' and platform_machine != 'aarch64' and sys_platform == 'linux'", + "python_full_version < '3.12' and sys_platform != 'darwin' and sys_platform != 'linux'", + "python_full_version == '3.12.*' and sys_platform == 'darwin'", + "python_full_version >= '3.13' and sys_platform == 'darwin'", + "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "python_full_version == '3.12.*' and platform_machine != 'aarch64' and sys_platform == 'linux'", + "python_full_version >= '3.13' and platform_machine != 'aarch64' and sys_platform == 'linux'", + "python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux'", + "python_full_version >= '3.13' and sys_platform != 'darwin' and sys_platform != 'linux'", +] + +[manifest] + +[[manifest.dependency-metadata]] +name = "uni" +version = "0.1.0" +requires-dist = ["torch>=2.0.1", "torchvision", "timm>=0.9.8", "numpy", "pandas", "scikit-learn", "tqdm", "transformers", "xformers ; sys_platform != 'darwin'"] + +[[package]] +name = "aiohappyeyeballs" +version = "2.6.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/26/30/f84a107a9c4331c14b2b586036f40965c128aa4fee4dda5d3d51cb14ad54/aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558", size = 22760 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8", size = 15265 }, +] + +[[package]] +name = "aiohttp" +version = "3.12.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohappyeyeballs" }, + { name = "aiosignal" }, + { name = "attrs" }, + { name = "frozenlist" }, + { name = "multidict" }, + { name = "propcache" }, + { name = "yarl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/23/28/2d96dffe4deb40faa7f5615b4aa96c87528e65837d8cb5385da4aecf1c07/aiohttp-3.12.6.tar.gz", hash = "sha256:37b1c6034a1e14764adad1829cd710543b1699d7985e1d336f0aa52a2dd76ba9", size = 7784449 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/07/f0/313bd623a40638ed65eddd930fbee3a81efd9c87441ea117067beb66b7e8/aiohttp-3.12.6-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ed4db015494a6d0acaadce035531f9fb321afab2075a4b348811e4f7795e87e6", size = 702633 }, + { url = "https://files.pythonhosted.org/packages/07/59/cd70b7798b5f6c13c65a692dbbbeacf4c085a9ce05a34363c4413384d895/aiohttp-3.12.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:59e19517abef2af49cff79b8a863497036ff401051c79d6a3b6149a48213a7be", size = 474917 }, + { url = "https://files.pythonhosted.org/packages/74/89/fe980184d1313669f731d7f32ce824a3ee1af50b4fe83fe723fcb56ca425/aiohttp-3.12.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d557918fefb29884335e1a257df6c961f35ba1caf8eddaabad762b3436cf87ff", size = 463178 }, + { url = "https://files.pythonhosted.org/packages/cb/2a/abe1c72f9b6959b5459f8b99bcdb661a7c2de7901b0c541c26996dd70006/aiohttp-3.12.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e4fb0d7f221c36ed8469c1d2d9a2bb6a27b543cf90aa46ca701f63fb83dd7ed", size = 1733367 }, + { url = "https://files.pythonhosted.org/packages/61/e2/992378c6b1e1b4beed78044ce5b70b749c269cc6e42472fc878339f90f4e/aiohttp-3.12.6-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:deddf6b1c83ce518a156b7597a0d7a1a7ec5c1d2c973ba3f1a23f18fa2b7d65e", size = 1682037 }, + { url = "https://files.pythonhosted.org/packages/64/11/620be3270f913c8d5ae4e9a88fa5ce80b7a7848324506da8b2916053f5f0/aiohttp-3.12.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eefd98dd043c33c45123c56a79c6c39acb628304337c90f16f33569cc3aa4ba6", size = 1780829 }, + { url = "https://files.pythonhosted.org/packages/c7/92/475e7c1781aa0907671b66355ae414f0d479f0bcf0929469ea44678b72c1/aiohttp-3.12.6-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:efbbde2297e4ab10d187103aba9b565277c85ac7d24d98cae201c033ce885504", size = 1819873 }, + { url = "https://files.pythonhosted.org/packages/70/04/9f4ad736af830d68dbd376db17f7294c648af393ec24a148bcd5cc2112c1/aiohttp-3.12.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2a74a566872f41247774980334e5b0309dac11b402e188bde6db8a57de4506cd", size = 1722335 }, + { url = "https://files.pythonhosted.org/packages/76/da/5ec4f8deacc4107bf95590d285879f4054d62ff9300a96d8abb4b1143384/aiohttp-3.12.6-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:24d19cbd1d21d207ee855500d2033f1852b4d2113a741246ff62eb16a3921306", size = 1659410 }, + { url = "https://files.pythonhosted.org/packages/85/38/30df9eedcfe28265f1efb1bfe9b19cc94c5a37aea5d2cd246dde8c8080e3/aiohttp-3.12.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:86fb0a5762f936606dcab1ca248f5053587a598ed44825f4744ce3c53ae9a2e9", size = 1707684 }, + { url = "https://files.pythonhosted.org/packages/e9/4c/c8d375fa9b6ea5c381747e5e56bc0249d33bb12c5d7171d4c1b4fcae02b4/aiohttp-3.12.6-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:d7ff55a38fc9851fa5cff41b30605534dfe4d57d02f79447abfed01499fe31d3", size = 1702843 }, + { url = "https://files.pythonhosted.org/packages/1e/3c/73b6e184df80ebc5ec07c6e9d398713c40091f6cf4a5e75eb93972ee35d6/aiohttp-3.12.6-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:545f89c389a47bac024655b5676658f35f80b0d007e4c3c7ff865d9aa3bf343a", size = 1683031 }, + { url = "https://files.pythonhosted.org/packages/9d/5a/827ca828af26ceeda69459c2848fc58e76adbbea0ad9994429ae885d1a33/aiohttp-3.12.6-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:25dac87ee297e2b5826ce8e96c7615ebe7a1613856b1614a207e3376b776021b", size = 1776629 }, + { url = "https://files.pythonhosted.org/packages/d6/07/8b9081655c08807fa49134b209eddc823e7501bbcf6b044f48a01f30a504/aiohttp-3.12.6-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:c1d8a4a5a7e28d8b9ec815ffecca8712b71130a4eee1c5b45e9f2cc4975f3f7c", size = 1797092 }, + { url = "https://files.pythonhosted.org/packages/5d/18/c761b934543512077c3de8d8f383bcafd94ada30c83273ea6ceeeda8aa2c/aiohttp-3.12.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bc4be1d8d68a62859f74f9ada9e174791895366601ce66342f54478d3518c8b3", size = 1710149 }, + { url = "https://files.pythonhosted.org/packages/e0/ba/730f75a17b613f2ecc336b698259140bbd439d8f7b14eae10aea14158085/aiohttp-3.12.6-cp311-cp311-win32.whl", hash = "sha256:a057680218430231eb6ab644d166b7ef398b3ffbac0232f4f789cdce9391400e", size = 420141 }, + { url = "https://files.pythonhosted.org/packages/df/71/8aaff029d07b15f7f79c484ca9b10f34cf8466d8dc4b13ecf32cc46b8de0/aiohttp-3.12.6-cp311-cp311-win_amd64.whl", hash = "sha256:8a88046a5adddf5d99f15a1920f6b8f659f46a4cfb5bfabbd668d06df045df7a", size = 444534 }, + { url = "https://files.pythonhosted.org/packages/e2/71/d4534c305623ba4e759caa381a5902713284f1ee52163d14894e60b3d254/aiohttp-3.12.6-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:cfbf8ed94b57e3b5a886bfe2a530c8eb067064cc4419fd94431a2cbeeddec54c", size = 693736 }, + { url = "https://files.pythonhosted.org/packages/0c/f4/0e4c010b669ef7418fcd5249edc1671bd07492be7989699b92cbc65e19c2/aiohttp-3.12.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:012ea107092d4465aeeb681d5b2fb8b51a847a72f0b71906f40876419fba1355", size = 468347 }, + { url = "https://files.pythonhosted.org/packages/b0/6e/6bc969bab1d4790548220b7bd061b711f246b708d7d8a6d88a0ecb04154c/aiohttp-3.12.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cdb03da5ecf74a331511604f3cf91563bf29127eabb28f4e16d390a73cb826da", size = 461191 }, + { url = "https://files.pythonhosted.org/packages/0a/56/de7ac8b49ce179618ede56f01f0050f75a270f3d9eb5d6905793e331a7d7/aiohttp-3.12.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ca81cb1e41d251cc193164409c0bbb0175e696a9997491a10db9171a2f70603", size = 1707982 }, + { url = "https://files.pythonhosted.org/packages/43/dd/74d8f791bf7832077ce5f7592126a64c6de57849182f730bb75dc7030ee7/aiohttp-3.12.6-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:15817882d25e840aba85d1f5706a7128350b81050f8ca9dabfc25a5f521a792c", size = 1690630 }, + { url = "https://files.pythonhosted.org/packages/10/f2/c5e96be25dd3efd0fe4b21a0c583fffadbadfc85f039cc0dd013e08bdc07/aiohttp-3.12.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:db5c402ea0aed10af2e54e5946bf32f3ebb02a7604eaaa4c41a608053889de4a", size = 1745727 }, + { url = "https://files.pythonhosted.org/packages/ad/19/6fb117cf46a99d302405012f05faf67e7ebae925e8ba5a0948f5c046a7b4/aiohttp-3.12.6-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ea77675818fd8cac28491d0d59582e5e2e5b14dbf5e21bef797aa5b23b5ca8b", size = 1791881 }, + { url = "https://files.pythonhosted.org/packages/78/2f/cdde703cbfd281aca8679b75289c3cde865a541efc593354e50c5c6b7c01/aiohttp-3.12.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c232720190ca4240c15abefc7b765e987ef88df44d2384612890db87b33898f3", size = 1711323 }, + { url = "https://files.pythonhosted.org/packages/e8/a1/edfdfe7ea9160f1bbf7bd00964da45ac290a5d19661761098eefa95ac400/aiohttp-3.12.6-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a2f3c974874bd0c76dfdcc60db5a6f96ca023a85318a5ac401603baa7e299272", size = 1627125 }, + { url = "https://files.pythonhosted.org/packages/5e/f0/de34cad1d44c6a7e4bffef9d42f982a28a4cdce8815733134aceb542be1d/aiohttp-3.12.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:25de52753386b0c16d5acd2153e7819f52c9e7fc05f5eca804adc174e99b735d", size = 1688185 }, + { url = "https://files.pythonhosted.org/packages/c7/46/e486289bc0a52d2cc4b87091de7428c4c4ddc76465c1aaa22eb953b8bcb7/aiohttp-3.12.6-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:3cc06a99e065ed7e766d2cd574671428261c1b8f30fedfbd91ab3c738fd9c08d", size = 1709637 }, + { url = "https://files.pythonhosted.org/packages/1f/f2/fe3d3955a2c9e78c308783ef0b1b53e5a3b56beb87133a52584e8dda52c0/aiohttp-3.12.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:aac87d78f55057ab48ddcc43055620546d40bbc0888d2658d8705d183c98f901", size = 1650291 }, + { url = "https://files.pythonhosted.org/packages/1c/68/2b425bd8031666be7db81e37918a9b3a9898e02b8d7e672737b05c55e2c5/aiohttp-3.12.6-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:de83f567e31418fd7bc22c5a03526a2b0a82e68c7a7fec23ef91a398228f559b", size = 1729852 }, + { url = "https://files.pythonhosted.org/packages/10/40/a14b0cf78531d504391a311c3e7c190f230cbf7dba5d4ccfbf53a5d121e5/aiohttp-3.12.6-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:fd1d6116c1364ab00ffed1654a01091dc7f897d315c5103bcc6e5ab7f70172c7", size = 1757824 }, + { url = "https://files.pythonhosted.org/packages/25/1d/250baf6961354772bf7447bb280dffa2df15c08875e535cf6a735a41373e/aiohttp-3.12.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:58f79b376a426961418df1d08656ec3a01494b7ba81824ae629e6636deddfff7", size = 1717436 }, + { url = "https://files.pythonhosted.org/packages/dc/43/9b9204284c08c244d89c69e3e556dfd7757e0393d4ef20a9238bf2643795/aiohttp-3.12.6-cp312-cp312-win32.whl", hash = "sha256:561f545dc062e6c31fc53535d8584c06516bda2fc37821a67a61b69202061e71", size = 414878 }, + { url = "https://files.pythonhosted.org/packages/fe/c1/8561f01a6386a7ecdc54aefff155aae51a349c98c04b1325619e12049fbc/aiohttp-3.12.6-cp312-cp312-win_amd64.whl", hash = "sha256:d83ab494eb583ba691af9d4d7c073987526bb9f73aa5a19907258ef3a1e39e8a", size = 440981 }, + { url = "https://files.pythonhosted.org/packages/be/5d/4db8a8972642779aa981aae2d71d88106a12f3f6a8354725ef4dbcf31a70/aiohttp-3.12.6-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7487f707a4b8167394f6afefa690198300d8a618505583eb536b92202bdec24d", size = 688139 }, + { url = "https://files.pythonhosted.org/packages/9b/b0/f0326159505f05a32e0dd858ca4770bdeb97900797d80ece9e8031f87c76/aiohttp-3.12.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9dd9211229fa2f474da01d42fafff196f607a63aaf12d8b34928c43a713eb6d5", size = 465812 }, + { url = "https://files.pythonhosted.org/packages/d1/3c/4abaf69965a63aac3bf3c9054c58b1eef68d6cf520ffeb593ed47e590da1/aiohttp-3.12.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3331ef09dd775302aa5f4d3170bd46659ad018843fab3656f5e72e3ff68df21f", size = 458109 }, + { url = "https://files.pythonhosted.org/packages/98/5f/8603860deada8e84ac5954bc736428ef370f8dd600b266c7d8177eea69ad/aiohttp-3.12.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c88ed8c54f7fd6102ef711d24710454707cde4bb3ffdec09982dcb3cb966a3e1", size = 1696923 }, + { url = "https://files.pythonhosted.org/packages/ec/40/209bb8dbb0b03f5758b7de70f86b7ac6acd8450a9bc4b4128cc5e89a20b2/aiohttp-3.12.6-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:148ffa6b2b825ff8520844ce23df9e2a5b969bb6917c4e35a832fbaa025d260d", size = 1678187 }, + { url = "https://files.pythonhosted.org/packages/26/bf/faa89212e33b6c6ba5913bd7319942f2955f0d199b7c6097896bac35ad6c/aiohttp-3.12.6-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e8da054804352e974f4349fb871b07c8ffa1978e64cfb455e88fbe6fbe4d6dcb", size = 1730257 }, + { url = "https://files.pythonhosted.org/packages/d9/0c/02df1921239913d91a74563547d8e1c79ec6caa052d0bddfbc48e09708a4/aiohttp-3.12.6-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7d162c4f87f9dcdc7151f6329438de96beb527820381e3159ce08544c57e9ced", size = 1779630 }, + { url = "https://files.pythonhosted.org/packages/d8/d0/c72d6b5a204291bbae5ae38fc367df9df11ce32dca6dcca6355d469c9c13/aiohttp-3.12.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da073f88270aa434ef16a78c21a4269c96c68badc2b9ad5011fa175c06143eee", size = 1701959 }, + { url = "https://files.pythonhosted.org/packages/b4/b1/2e2cc4bb3de9d0b185a5c5b6b9d04e3a37c79e52529c634a962ca7a22bfe/aiohttp-3.12.6-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b2e026a9f9ac0df70f14ca5dcaf1f83a55b678e51aa6515d710dd879d2691fd7", size = 1615662 }, + { url = "https://files.pythonhosted.org/packages/bc/55/00c119c8ce2d65879b7b6d64b5a344df3ee8845f0d2a11d190376c9e7256/aiohttp-3.12.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5b700cf48fd04b4328965d1afe01f835fe6cdecc3b85ca2d950431e5cc0647f7", size = 1668684 }, + { url = "https://files.pythonhosted.org/packages/b8/be/59bc7538ccaff6fe9cf0b3a27f976d8b2729150b700dc37beef71705f009/aiohttp-3.12.6-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:38af291559401d13eb90259ba79ef6ac537ae6b5bdb1251604606a88cd0fd5e0", size = 1700350 }, + { url = "https://files.pythonhosted.org/packages/a2/3c/bcfc532cf09755c5d094e320ba7e9e7a6b977d6487b211278a5d400d0649/aiohttp-3.12.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:6860351cfba0196db2edc387cfeddaf1dae443e55f261ea2bcb77fecb33aae34", size = 1642945 }, + { url = "https://files.pythonhosted.org/packages/44/27/ebc660cb7624ce8d6b71486490478bb52784074cc46f5fed8fbf5f0306d2/aiohttp-3.12.6-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:06f20adcdc4f383aeb7ce884705faea44c0376cde5cdee4d32ef62d6cb1f97cc", size = 1719080 }, + { url = "https://files.pythonhosted.org/packages/81/6e/fd000fa2708cb3b887c0fe8a144f926ca34960a80ed1c44d3606027fd831/aiohttp-3.12.6-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:a52aa39eb1160775a6e80e3025c990e8872c8927c5dd4b51304788bc149b9549", size = 1752550 }, + { url = "https://files.pythonhosted.org/packages/ee/2d/5a0bd6d09ea38fcb3ec683425b25946156b99ab451c69ef84ea3d03b6eaf/aiohttp-3.12.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:52ce7e90ee9dd25bcd2ed4513e650cc4f9a03bef07a39193b82fb58892004bd6", size = 1701441 }, + { url = "https://files.pythonhosted.org/packages/bd/7b/db64cfd8fd522de73b803b600d967cc2821250f82ba97892a90c4f7e204e/aiohttp-3.12.6-cp313-cp313-win32.whl", hash = "sha256:259269870d9783de87c0430760b2498b770201ead3e11ee86761d268ce5d196a", size = 413900 }, + { url = "https://files.pythonhosted.org/packages/7f/d6/4680e3601edf5ec0e1e56cca7746f0de9b9758a33b88067b1935e95f7005/aiohttp-3.12.6-cp313-cp313-win_amd64.whl", hash = "sha256:938afd243c9ee76a6d78fad10ecca14b88b48b71553e0e9c74b8098efff5ddf8", size = 439844 }, +] + +[[package]] +name = "aiosignal" +version = "1.3.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "frozenlist" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ba/b5/6d55e80f6d8a08ce22b982eafa278d823b541c925f11ee774b0b9c43473d/aiosignal-1.3.2.tar.gz", hash = "sha256:a8c255c66fafb1e499c9351d0bf32ff2d8a0321595ebac3b93713656d2436f54", size = 19424 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/6a/bc7e17a3e87a2985d3e8f4da4cd0f481060eb78fb08596c42be62c90a4d9/aiosignal-1.3.2-py2.py3-none-any.whl", hash = "sha256:45cde58e409a301715980c2b01d0c28bdde3770d8290b5eb2173759d9acb31a5", size = 7597 }, +] + +[[package]] +name = "annotated-types" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 }, +] + +[[package]] +name = "appnope" +version = "0.1.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/35/5d/752690df9ef5b76e169e68d6a129fa6d08a7100ca7f754c89495db3c6019/appnope-0.1.4.tar.gz", hash = "sha256:1de3860566df9caf38f01f86f65e0e13e379af54f9e4bee1e66b48f2efffd1ee", size = 4170 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/81/29/5ecc3a15d5a33e31b26c11426c45c501e439cb865d0bff96315d86443b78/appnope-0.1.4-py2.py3-none-any.whl", hash = "sha256:502575ee11cd7a28c0205f379b525beefebab9d161b7c964670864014ed7213c", size = 4321 }, +] + +[[package]] +name = "asttokens" +version = "3.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4a/e7/82da0a03e7ba5141f05cce0d302e6eed121ae055e0456ca228bf693984bc/asttokens-3.0.0.tar.gz", hash = "sha256:0dcd8baa8d62b0c1d118b399b2ddba3c4aff271d0d7a9e0d4c1681c79035bbc7", size = 61978 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/25/8a/c46dcc25341b5bce5472c718902eb3d38600a903b14fa6aeecef3f21a46f/asttokens-3.0.0-py3-none-any.whl", hash = "sha256:e3078351a059199dd5138cb1c706e6430c05eff2ff136af5eb4790f9d28932e2", size = 26918 }, +] + +[[package]] +name = "attrs" +version = "25.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5a/b0/1367933a8532ee6ff8d63537de4f1177af4bff9f3e829baf7331f595bb24/attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b", size = 812032 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/77/06/bb80f5f86020c4551da315d78b3ab75e8228f89f0162f2c3a819e407941a/attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3", size = 63815 }, +] + +[[package]] +name = "autograd" +version = "1.8.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/67/1c/3c24ec03c8ba4decc742b1df5a10c52f98c84ca8797757f313e7bdcdf276/autograd-1.8.0.tar.gz", hash = "sha256:107374ded5b09fc8643ac925348c0369e7b0e73bbed9565ffd61b8fd04425683", size = 2562146 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/84/ea/e16f0c423f7d83cf8b79cae9452040fb7b2e020c7439a167ee7c317de448/autograd-1.8.0-py3-none-any.whl", hash = "sha256:4ab9084294f814cf56c280adbe19612546a35574d67c574b04933c7d2ecb7d78", size = 51478 }, +] + +[[package]] +name = "autograd-gamma" +version = "0.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "autograd" }, + { name = "scipy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/85/ae/7f2031ea76140444b2453fa139041e5afd4a09fc5300cfefeb1103291f80/autograd-gamma-0.5.0.tar.gz", hash = "sha256:f27abb7b8bb9cffc8badcbf59f3fe44a9db39e124ecacf1992b6d952934ac9c4", size = 3952 } + +[[package]] +name = "beartype" +version = "0.21.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0d/f9/21e5a9c731e14f08addd53c71fea2e70794e009de5b98e6a2c3d2f3015d6/beartype-0.21.0.tar.gz", hash = "sha256:f9a5078f5ce87261c2d22851d19b050b64f6a805439e8793aecf01ce660d3244", size = 1437066 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/94/31/87045d1c66ee10a52486c9d2047bc69f00f2689f69401bb1e998afb4b205/beartype-0.21.0-py3-none-any.whl", hash = "sha256:b6a1bd56c72f31b0a496a36cc55df6e2f475db166ad07fa4acc7e74f4c7f34c0", size = 1191340 }, +] + +[[package]] +name = "beautifulsoup4" +version = "4.13.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "soupsieve" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d8/e4/0c4c39e18fd76d6a628d4dd8da40543d136ce2d1752bd6eeeab0791f4d6b/beautifulsoup4-4.13.4.tar.gz", hash = "sha256:dbb3c4e1ceae6aefebdaf2423247260cd062430a410e38c66f2baa50a8437195", size = 621067 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/50/cd/30110dc0ffcf3b131156077b90e9f60ed75711223f306da4db08eff8403b/beautifulsoup4-4.13.4-py3-none-any.whl", hash = "sha256:9bbbb14bfde9d79f38b8cd5f8c7c85f4b8f2523190ebed90e950a8dea4cb1c4b", size = 187285 }, +] + +[[package]] +name = "braceexpand" +version = "0.1.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/54/93/badd4f5ccf25209f3fef2573073da9fe4a45a3da99fca2f800f942130c0f/braceexpand-0.1.7.tar.gz", hash = "sha256:e6e539bd20eaea53547472ff94f4fb5c3d3bf9d0a89388c4b56663aba765f705", size = 7777 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fa/93/e8c04e80e82391a6e51f218ca49720f64236bc824e92152a2633b74cf7ab/braceexpand-0.1.7-py2.py3-none-any.whl", hash = "sha256:91332d53de7828103dcae5773fb43bc34950b0c8160e35e0f44c4427a3b85014", size = 5923 }, +] + +[[package]] +name = "causal-conv1d" +version = "1.5.0.post8" +source = { git = "https://github.com/KatherLab/causal-conv1d.git?rev=55b4626e1a2d3d6b939811725f2f3ef65b7b3ff1#55b4626e1a2d3d6b939811725f2f3ef65b7b3ff1" } +dependencies = [ + { name = "ninja" }, + { name = "packaging" }, + { name = "torch" }, +] + +[[package]] +name = "certifi" +version = "2025.4.26" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e8/9e/c05b3920a3b7d20d3d3310465f50348e5b3694f4f88c6daf736eef3024c4/certifi-2025.4.26.tar.gz", hash = "sha256:0a816057ea3cdefcef70270d2c515e4506bbc954f417fa5ade2021213bb8f0c6", size = 160705 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4a/7e/3db2bd1b1f9e95f7cddca6d6e75e2f2bd9f51b1246e546d88addca0106bd/certifi-2025.4.26-py3-none-any.whl", hash = "sha256:30350364dfe371162649852c63336a15c70c6510c2ad5015b21c2345311805f3", size = 159618 }, +] + +[[package]] +name = "cffi" +version = "1.17.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pycparser" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fc/97/c783634659c2920c3fc70419e3af40972dbaf758daa229a7d6ea6135c90d/cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824", size = 516621 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6b/f4/927e3a8899e52a27fa57a48607ff7dc91a9ebe97399b357b85a0c7892e00/cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401", size = 182264 }, + { url = "https://files.pythonhosted.org/packages/6c/f5/6c3a8efe5f503175aaddcbea6ad0d2c96dad6f5abb205750d1b3df44ef29/cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf", size = 178651 }, + { url = "https://files.pythonhosted.org/packages/94/dd/a3f0118e688d1b1a57553da23b16bdade96d2f9bcda4d32e7d2838047ff7/cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4", size = 445259 }, + { url = "https://files.pythonhosted.org/packages/2e/ea/70ce63780f096e16ce8588efe039d3c4f91deb1dc01e9c73a287939c79a6/cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41", size = 469200 }, + { url = "https://files.pythonhosted.org/packages/1c/a0/a4fa9f4f781bda074c3ddd57a572b060fa0df7655d2a4247bbe277200146/cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1", size = 477235 }, + { url = "https://files.pythonhosted.org/packages/62/12/ce8710b5b8affbcdd5c6e367217c242524ad17a02fe5beec3ee339f69f85/cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6", size = 459721 }, + { url = "https://files.pythonhosted.org/packages/ff/6b/d45873c5e0242196f042d555526f92aa9e0c32355a1be1ff8c27f077fd37/cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d", size = 467242 }, + { url = "https://files.pythonhosted.org/packages/1a/52/d9a0e523a572fbccf2955f5abe883cfa8bcc570d7faeee06336fbd50c9fc/cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6", size = 477999 }, + { url = "https://files.pythonhosted.org/packages/44/74/f2a2460684a1a2d00ca799ad880d54652841a780c4c97b87754f660c7603/cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f", size = 454242 }, + { url = "https://files.pythonhosted.org/packages/f8/4a/34599cac7dfcd888ff54e801afe06a19c17787dfd94495ab0c8d35fe99fb/cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b", size = 478604 }, + { url = "https://files.pythonhosted.org/packages/34/33/e1b8a1ba29025adbdcda5fb3a36f94c03d771c1b7b12f726ff7fef2ebe36/cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655", size = 171727 }, + { url = "https://files.pythonhosted.org/packages/3d/97/50228be003bb2802627d28ec0627837ac0bf35c90cf769812056f235b2d1/cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0", size = 181400 }, + { url = "https://files.pythonhosted.org/packages/5a/84/e94227139ee5fb4d600a7a4927f322e1d4aea6fdc50bd3fca8493caba23f/cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4", size = 183178 }, + { url = "https://files.pythonhosted.org/packages/da/ee/fb72c2b48656111c4ef27f0f91da355e130a923473bf5ee75c5643d00cca/cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c", size = 178840 }, + { url = "https://files.pythonhosted.org/packages/cc/b6/db007700f67d151abadf508cbfd6a1884f57eab90b1bb985c4c8c02b0f28/cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36", size = 454803 }, + { url = "https://files.pythonhosted.org/packages/1a/df/f8d151540d8c200eb1c6fba8cd0dfd40904f1b0682ea705c36e6c2e97ab3/cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5", size = 478850 }, + { url = "https://files.pythonhosted.org/packages/28/c0/b31116332a547fd2677ae5b78a2ef662dfc8023d67f41b2a83f7c2aa78b1/cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff", size = 485729 }, + { url = "https://files.pythonhosted.org/packages/91/2b/9a1ddfa5c7f13cab007a2c9cc295b70fbbda7cb10a286aa6810338e60ea1/cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99", size = 471256 }, + { url = "https://files.pythonhosted.org/packages/b2/d5/da47df7004cb17e4955df6a43d14b3b4ae77737dff8bf7f8f333196717bf/cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93", size = 479424 }, + { url = "https://files.pythonhosted.org/packages/0b/ac/2a28bcf513e93a219c8a4e8e125534f4f6db03e3179ba1c45e949b76212c/cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3", size = 484568 }, + { url = "https://files.pythonhosted.org/packages/d4/38/ca8a4f639065f14ae0f1d9751e70447a261f1a30fa7547a828ae08142465/cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8", size = 488736 }, + { url = "https://files.pythonhosted.org/packages/86/c5/28b2d6f799ec0bdecf44dced2ec5ed43e0eb63097b0f58c293583b406582/cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65", size = 172448 }, + { url = "https://files.pythonhosted.org/packages/50/b9/db34c4755a7bd1cb2d1603ac3863f22bcecbd1ba29e5ee841a4bc510b294/cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903", size = 181976 }, + { url = "https://files.pythonhosted.org/packages/8d/f8/dd6c246b148639254dad4d6803eb6a54e8c85c6e11ec9df2cffa87571dbe/cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e", size = 182989 }, + { url = "https://files.pythonhosted.org/packages/8b/f1/672d303ddf17c24fc83afd712316fda78dc6fce1cd53011b839483e1ecc8/cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2", size = 178802 }, + { url = "https://files.pythonhosted.org/packages/0e/2d/eab2e858a91fdff70533cab61dcff4a1f55ec60425832ddfdc9cd36bc8af/cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3", size = 454792 }, + { url = "https://files.pythonhosted.org/packages/75/b2/fbaec7c4455c604e29388d55599b99ebcc250a60050610fadde58932b7ee/cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683", size = 478893 }, + { url = "https://files.pythonhosted.org/packages/4f/b7/6e4a2162178bf1935c336d4da8a9352cccab4d3a5d7914065490f08c0690/cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5", size = 485810 }, + { url = "https://files.pythonhosted.org/packages/c7/8a/1d0e4a9c26e54746dc08c2c6c037889124d4f59dffd853a659fa545f1b40/cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4", size = 471200 }, + { url = "https://files.pythonhosted.org/packages/26/9f/1aab65a6c0db35f43c4d1b4f580e8df53914310afc10ae0397d29d697af4/cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd", size = 479447 }, + { url = "https://files.pythonhosted.org/packages/5f/e4/fb8b3dd8dc0e98edf1135ff067ae070bb32ef9d509d6cb0f538cd6f7483f/cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed", size = 484358 }, + { url = "https://files.pythonhosted.org/packages/f1/47/d7145bf2dc04684935d57d67dff9d6d795b2ba2796806bb109864be3a151/cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9", size = 488469 }, + { url = "https://files.pythonhosted.org/packages/bf/ee/f94057fa6426481d663b88637a9a10e859e492c73d0384514a17d78ee205/cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d", size = 172475 }, + { url = "https://files.pythonhosted.org/packages/7c/fc/6a8cb64e5f0324877d503c854da15d76c1e50eb722e320b15345c4d0c6de/cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a", size = 182009 }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e4/33/89c2ced2b67d1c2a61c19c6751aa8902d46ce3dacb23600a283619f5a12d/charset_normalizer-3.4.2.tar.gz", hash = "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63", size = 126367 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/05/85/4c40d00dcc6284a1c1ad5de5e0996b06f39d8232f1031cd23c2f5c07ee86/charset_normalizer-3.4.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:be1e352acbe3c78727a16a455126d9ff83ea2dfdcbc83148d2982305a04714c2", size = 198794 }, + { url = "https://files.pythonhosted.org/packages/41/d9/7a6c0b9db952598e97e93cbdfcb91bacd89b9b88c7c983250a77c008703c/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa88ca0b1932e93f2d961bf3addbb2db902198dca337d88c89e1559e066e7645", size = 142846 }, + { url = "https://files.pythonhosted.org/packages/66/82/a37989cda2ace7e37f36c1a8ed16c58cf48965a79c2142713244bf945c89/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d524ba3f1581b35c03cb42beebab4a13e6cdad7b36246bd22541fa585a56cccd", size = 153350 }, + { url = "https://files.pythonhosted.org/packages/df/68/a576b31b694d07b53807269d05ec3f6f1093e9545e8607121995ba7a8313/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28a1005facc94196e1fb3e82a3d442a9d9110b8434fc1ded7a24a2983c9888d8", size = 145657 }, + { url = "https://files.pythonhosted.org/packages/92/9b/ad67f03d74554bed3aefd56fe836e1623a50780f7c998d00ca128924a499/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdb20a30fe1175ecabed17cbf7812f7b804b8a315a25f24678bcdf120a90077f", size = 147260 }, + { url = "https://files.pythonhosted.org/packages/a6/e6/8aebae25e328160b20e31a7e9929b1578bbdc7f42e66f46595a432f8539e/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0f5d9ed7f254402c9e7d35d2f5972c9bbea9040e99cd2861bd77dc68263277c7", size = 149164 }, + { url = "https://files.pythonhosted.org/packages/8b/f2/b3c2f07dbcc248805f10e67a0262c93308cfa149a4cd3d1fe01f593e5fd2/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:efd387a49825780ff861998cd959767800d54f8308936b21025326de4b5a42b9", size = 144571 }, + { url = "https://files.pythonhosted.org/packages/60/5b/c3f3a94bc345bc211622ea59b4bed9ae63c00920e2e8f11824aa5708e8b7/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f0aa37f3c979cf2546b73e8222bbfa3dc07a641585340179d768068e3455e544", size = 151952 }, + { url = "https://files.pythonhosted.org/packages/e2/4d/ff460c8b474122334c2fa394a3f99a04cf11c646da895f81402ae54f5c42/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e70e990b2137b29dc5564715de1e12701815dacc1d056308e2b17e9095372a82", size = 155959 }, + { url = "https://files.pythonhosted.org/packages/a2/2b/b964c6a2fda88611a1fe3d4c400d39c66a42d6c169c924818c848f922415/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0c8c57f84ccfc871a48a47321cfa49ae1df56cd1d965a09abe84066f6853b9c0", size = 153030 }, + { url = "https://files.pythonhosted.org/packages/59/2e/d3b9811db26a5ebf444bc0fa4f4be5aa6d76fc6e1c0fd537b16c14e849b6/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6b66f92b17849b85cad91259efc341dce9c1af48e2173bf38a85c6329f1033e5", size = 148015 }, + { url = "https://files.pythonhosted.org/packages/90/07/c5fd7c11eafd561bb51220d600a788f1c8d77c5eef37ee49454cc5c35575/charset_normalizer-3.4.2-cp311-cp311-win32.whl", hash = "sha256:daac4765328a919a805fa5e2720f3e94767abd632ae410a9062dff5412bae65a", size = 98106 }, + { url = "https://files.pythonhosted.org/packages/a8/05/5e33dbef7e2f773d672b6d79f10ec633d4a71cd96db6673625838a4fd532/charset_normalizer-3.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:e53efc7c7cee4c1e70661e2e112ca46a575f90ed9ae3fef200f2a25e954f4b28", size = 105402 }, + { url = "https://files.pythonhosted.org/packages/d7/a4/37f4d6035c89cac7930395a35cc0f1b872e652eaafb76a6075943754f095/charset_normalizer-3.4.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0c29de6a1a95f24b9a1aa7aefd27d2487263f00dfd55a77719b530788f75cff7", size = 199936 }, + { url = "https://files.pythonhosted.org/packages/ee/8a/1a5e33b73e0d9287274f899d967907cd0bf9c343e651755d9307e0dbf2b3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cddf7bd982eaa998934a91f69d182aec997c6c468898efe6679af88283b498d3", size = 143790 }, + { url = "https://files.pythonhosted.org/packages/66/52/59521f1d8e6ab1482164fa21409c5ef44da3e9f653c13ba71becdd98dec3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcbe676a55d7445b22c10967bceaaf0ee69407fbe0ece4d032b6eb8d4565982a", size = 153924 }, + { url = "https://files.pythonhosted.org/packages/86/2d/fb55fdf41964ec782febbf33cb64be480a6b8f16ded2dbe8db27a405c09f/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d41c4d287cfc69060fa91cae9683eacffad989f1a10811995fa309df656ec214", size = 146626 }, + { url = "https://files.pythonhosted.org/packages/8c/73/6ede2ec59bce19b3edf4209d70004253ec5f4e319f9a2e3f2f15601ed5f7/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e594135de17ab3866138f496755f302b72157d115086d100c3f19370839dd3a", size = 148567 }, + { url = "https://files.pythonhosted.org/packages/09/14/957d03c6dc343c04904530b6bef4e5efae5ec7d7990a7cbb868e4595ee30/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf713fe9a71ef6fd5adf7a79670135081cd4431c2943864757f0fa3a65b1fafd", size = 150957 }, + { url = "https://files.pythonhosted.org/packages/0d/c8/8174d0e5c10ccebdcb1b53cc959591c4c722a3ad92461a273e86b9f5a302/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a370b3e078e418187da8c3674eddb9d983ec09445c99a3a263c2011993522981", size = 145408 }, + { url = "https://files.pythonhosted.org/packages/58/aa/8904b84bc8084ac19dc52feb4f5952c6df03ffb460a887b42615ee1382e8/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a955b438e62efdf7e0b7b52a64dc5c3396e2634baa62471768a64bc2adb73d5c", size = 153399 }, + { url = "https://files.pythonhosted.org/packages/c2/26/89ee1f0e264d201cb65cf054aca6038c03b1a0c6b4ae998070392a3ce605/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7222ffd5e4de8e57e03ce2cef95a4c43c98fcb72ad86909abdfc2c17d227fc1b", size = 156815 }, + { url = "https://files.pythonhosted.org/packages/fd/07/68e95b4b345bad3dbbd3a8681737b4338ff2c9df29856a6d6d23ac4c73cb/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:bee093bf902e1d8fc0ac143c88902c3dfc8941f7ea1d6a8dd2bcb786d33db03d", size = 154537 }, + { url = "https://files.pythonhosted.org/packages/77/1a/5eefc0ce04affb98af07bc05f3bac9094513c0e23b0562d64af46a06aae4/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb8adb91d11846ee08bec4c8236c8549ac721c245678282dcb06b221aab59f", size = 149565 }, + { url = "https://files.pythonhosted.org/packages/37/a0/2410e5e6032a174c95e0806b1a6585eb21e12f445ebe239fac441995226a/charset_normalizer-3.4.2-cp312-cp312-win32.whl", hash = "sha256:db4c7bf0e07fc3b7d89ac2a5880a6a8062056801b83ff56d8464b70f65482b6c", size = 98357 }, + { url = "https://files.pythonhosted.org/packages/6c/4f/c02d5c493967af3eda9c771ad4d2bbc8df6f99ddbeb37ceea6e8716a32bc/charset_normalizer-3.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:5a9979887252a82fefd3d3ed2a8e3b937a7a809f65dcb1e068b090e165bbe99e", size = 105776 }, + { url = "https://files.pythonhosted.org/packages/ea/12/a93df3366ed32db1d907d7593a94f1fe6293903e3e92967bebd6950ed12c/charset_normalizer-3.4.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0", size = 199622 }, + { url = "https://files.pythonhosted.org/packages/04/93/bf204e6f344c39d9937d3c13c8cd5bbfc266472e51fc8c07cb7f64fcd2de/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf", size = 143435 }, + { url = "https://files.pythonhosted.org/packages/22/2a/ea8a2095b0bafa6c5b5a55ffdc2f924455233ee7b91c69b7edfcc9e02284/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e", size = 153653 }, + { url = "https://files.pythonhosted.org/packages/b6/57/1b090ff183d13cef485dfbe272e2fe57622a76694061353c59da52c9a659/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1", size = 146231 }, + { url = "https://files.pythonhosted.org/packages/e2/28/ffc026b26f441fc67bd21ab7f03b313ab3fe46714a14b516f931abe1a2d8/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c", size = 148243 }, + { url = "https://files.pythonhosted.org/packages/c0/0f/9abe9bd191629c33e69e47c6ef45ef99773320e9ad8e9cb08b8ab4a8d4cb/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691", size = 150442 }, + { url = "https://files.pythonhosted.org/packages/67/7c/a123bbcedca91d5916c056407f89a7f5e8fdfce12ba825d7d6b9954a1a3c/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0", size = 145147 }, + { url = "https://files.pythonhosted.org/packages/ec/fe/1ac556fa4899d967b83e9893788e86b6af4d83e4726511eaaad035e36595/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b", size = 153057 }, + { url = "https://files.pythonhosted.org/packages/2b/ff/acfc0b0a70b19e3e54febdd5301a98b72fa07635e56f24f60502e954c461/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff", size = 156454 }, + { url = "https://files.pythonhosted.org/packages/92/08/95b458ce9c740d0645feb0e96cea1f5ec946ea9c580a94adfe0b617f3573/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b", size = 154174 }, + { url = "https://files.pythonhosted.org/packages/78/be/8392efc43487ac051eee6c36d5fbd63032d78f7728cb37aebcc98191f1ff/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148", size = 149166 }, + { url = "https://files.pythonhosted.org/packages/44/96/392abd49b094d30b91d9fbda6a69519e95802250b777841cf3bda8fe136c/charset_normalizer-3.4.2-cp313-cp313-win32.whl", hash = "sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7", size = 98064 }, + { url = "https://files.pythonhosted.org/packages/e9/b0/0200da600134e001d91851ddc797809e2fe0ea72de90e09bec5a2fbdaccb/charset_normalizer-3.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980", size = 105641 }, + { url = "https://files.pythonhosted.org/packages/20/94/c5790835a017658cbfabd07f3bfb549140c3ac458cfc196323996b10095a/charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0", size = 52626 }, +] + +[[package]] +name = "click" +version = "8.2.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/60/6c/8ca2efa64cf75a977a0d7fac081354553ebe483345c734fb6b6515d96bbc/click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202", size = 286342 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/85/32/10bb5764d90a8eee674e9dc6f4db6a0ab47c8c4d0d83c27f7c39ac415a4d/click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b", size = 102215 }, +] + +[[package]] +name = "cobra" +version = "0.1.0" +source = { git = "https://github.com/KatherLab/COBRA.git?rev=f1a576e1133330ffc2d1df6ee110701921c7b7c9#f1a576e1133330ffc2d1df6ee110701921c7b7c9" } +dependencies = [ + { name = "causal-conv1d" }, + { name = "einops" }, + { name = "h5py" }, + { name = "huggingface-hub" }, + { name = "jinja2" }, + { name = "mamba-ssm" }, + { name = "matplotlib" }, + { name = "numpy" }, + { name = "openpyxl" }, + { name = "openslide-bin" }, + { name = "openslide-python" }, + { name = "pandas" }, + { name = "pytorch-lightning" }, + { name = "pyyaml" }, + { name = "scikit-learn" }, + { name = "torch" }, + { name = "torchmetrics" }, + { name = "torchvision" }, + { name = "tqdm" }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, +] + +[[package]] +name = "comm" +version = "0.2.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e9/a8/fb783cb0abe2b5fded9f55e5703015cdf1c9c85b3669087c538dd15a6a86/comm-0.2.2.tar.gz", hash = "sha256:3fd7a84065306e07bea1773df6eb8282de51ba82f77c72f9c85716ab11fe980e", size = 6210 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e6/75/49e5bfe642f71f272236b5b2d2691cf915a7283cc0ceda56357b61daa538/comm-0.2.2-py3-none-any.whl", hash = "sha256:e6fb86cb70ff661ee8c9c14e7d36d6de3b4066f1441be4063df9c5009f0a64d3", size = 7180 }, +] + +[[package]] +name = "conch" +version = "0.1.0" +source = { git = "https://github.com/Mahmoodlab/CONCH.git?rev=02d6ac59cc20874bff0f581de258c2b257f69a84#02d6ac59cc20874bff0f581de258c2b257f69a84" } +dependencies = [ + { name = "ftfy" }, + { name = "h5py" }, + { name = "numpy" }, + { name = "pandas" }, + { name = "regex" }, + { name = "scikit-learn" }, + { name = "timm" }, + { name = "tokenizers" }, + { name = "torch" }, + { name = "torchvision" }, + { name = "transformers" }, +] + +[[package]] +name = "contourpy" +version = "1.3.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/54/eb9bfc647b19f2009dd5c7f5ec51c4e6ca831725f1aea7a993034f483147/contourpy-1.3.2.tar.gz", hash = "sha256:b6945942715a034c671b7fc54f9588126b0b8bf23db2696e3ca8328f3ff0ab54", size = 13466130 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/b9/ede788a0b56fc5b071639d06c33cb893f68b1178938f3425debebe2dab78/contourpy-1.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6a37a2fb93d4df3fc4c0e363ea4d16f83195fc09c891bc8ce072b9d084853445", size = 269636 }, + { url = "https://files.pythonhosted.org/packages/e6/75/3469f011d64b8bbfa04f709bfc23e1dd71be54d05b1b083be9f5b22750d1/contourpy-1.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b7cd50c38f500bbcc9b6a46643a40e0913673f869315d8e70de0438817cb7773", size = 254636 }, + { url = "https://files.pythonhosted.org/packages/8d/2f/95adb8dae08ce0ebca4fd8e7ad653159565d9739128b2d5977806656fcd2/contourpy-1.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d6658ccc7251a4433eebd89ed2672c2ed96fba367fd25ca9512aa92a4b46c4f1", size = 313053 }, + { url = "https://files.pythonhosted.org/packages/c3/a6/8ccf97a50f31adfa36917707fe39c9a0cbc24b3bbb58185577f119736cc9/contourpy-1.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:70771a461aaeb335df14deb6c97439973d253ae70660ca085eec25241137ef43", size = 352985 }, + { url = "https://files.pythonhosted.org/packages/1d/b6/7925ab9b77386143f39d9c3243fdd101621b4532eb126743201160ffa7e6/contourpy-1.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65a887a6e8c4cd0897507d814b14c54a8c2e2aa4ac9f7686292f9769fcf9a6ab", size = 323750 }, + { url = "https://files.pythonhosted.org/packages/c2/f3/20c5d1ef4f4748e52d60771b8560cf00b69d5c6368b5c2e9311bcfa2a08b/contourpy-1.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3859783aefa2b8355697f16642695a5b9792e7a46ab86da1118a4a23a51a33d7", size = 326246 }, + { url = "https://files.pythonhosted.org/packages/8c/e5/9dae809e7e0b2d9d70c52b3d24cba134dd3dad979eb3e5e71f5df22ed1f5/contourpy-1.3.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:eab0f6db315fa4d70f1d8ab514e527f0366ec021ff853d7ed6a2d33605cf4b83", size = 1308728 }, + { url = "https://files.pythonhosted.org/packages/e2/4a/0058ba34aeea35c0b442ae61a4f4d4ca84d6df8f91309bc2d43bb8dd248f/contourpy-1.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d91a3ccc7fea94ca0acab82ceb77f396d50a1f67412efe4c526f5d20264e6ecd", size = 1375762 }, + { url = "https://files.pythonhosted.org/packages/09/33/7174bdfc8b7767ef2c08ed81244762d93d5c579336fc0b51ca57b33d1b80/contourpy-1.3.2-cp311-cp311-win32.whl", hash = "sha256:1c48188778d4d2f3d48e4643fb15d8608b1d01e4b4d6b0548d9b336c28fc9b6f", size = 178196 }, + { url = "https://files.pythonhosted.org/packages/5e/fe/4029038b4e1c4485cef18e480b0e2cd2d755448bb071eb9977caac80b77b/contourpy-1.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:5ebac872ba09cb8f2131c46b8739a7ff71de28a24c869bcad554477eb089a878", size = 222017 }, + { url = "https://files.pythonhosted.org/packages/34/f7/44785876384eff370c251d58fd65f6ad7f39adce4a093c934d4a67a7c6b6/contourpy-1.3.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4caf2bcd2969402bf77edc4cb6034c7dd7c0803213b3523f111eb7460a51b8d2", size = 271580 }, + { url = "https://files.pythonhosted.org/packages/93/3b/0004767622a9826ea3d95f0e9d98cd8729015768075d61f9fea8eeca42a8/contourpy-1.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:82199cb78276249796419fe36b7386bd8d2cc3f28b3bc19fe2454fe2e26c4c15", size = 255530 }, + { url = "https://files.pythonhosted.org/packages/e7/bb/7bd49e1f4fa805772d9fd130e0d375554ebc771ed7172f48dfcd4ca61549/contourpy-1.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:106fab697af11456fcba3e352ad50effe493a90f893fca6c2ca5c033820cea92", size = 307688 }, + { url = "https://files.pythonhosted.org/packages/fc/97/e1d5dbbfa170725ef78357a9a0edc996b09ae4af170927ba8ce977e60a5f/contourpy-1.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d14f12932a8d620e307f715857107b1d1845cc44fdb5da2bc8e850f5ceba9f87", size = 347331 }, + { url = "https://files.pythonhosted.org/packages/6f/66/e69e6e904f5ecf6901be3dd16e7e54d41b6ec6ae3405a535286d4418ffb4/contourpy-1.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:532fd26e715560721bb0d5fc7610fce279b3699b018600ab999d1be895b09415", size = 318963 }, + { url = "https://files.pythonhosted.org/packages/a8/32/b8a1c8965e4f72482ff2d1ac2cd670ce0b542f203c8e1d34e7c3e6925da7/contourpy-1.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f26b383144cf2d2c29f01a1e8170f50dacf0eac02d64139dcd709a8ac4eb3cfe", size = 323681 }, + { url = "https://files.pythonhosted.org/packages/30/c6/12a7e6811d08757c7162a541ca4c5c6a34c0f4e98ef2b338791093518e40/contourpy-1.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c49f73e61f1f774650a55d221803b101d966ca0c5a2d6d5e4320ec3997489441", size = 1308674 }, + { url = "https://files.pythonhosted.org/packages/2a/8a/bebe5a3f68b484d3a2b8ffaf84704b3e343ef1addea528132ef148e22b3b/contourpy-1.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3d80b2c0300583228ac98d0a927a1ba6a2ba6b8a742463c564f1d419ee5b211e", size = 1380480 }, + { url = "https://files.pythonhosted.org/packages/34/db/fcd325f19b5978fb509a7d55e06d99f5f856294c1991097534360b307cf1/contourpy-1.3.2-cp312-cp312-win32.whl", hash = "sha256:90df94c89a91b7362e1142cbee7568f86514412ab8a2c0d0fca72d7e91b62912", size = 178489 }, + { url = "https://files.pythonhosted.org/packages/01/c8/fadd0b92ffa7b5eb5949bf340a63a4a496a6930a6c37a7ba0f12acb076d6/contourpy-1.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:8c942a01d9163e2e5cfb05cb66110121b8d07ad438a17f9e766317bcb62abf73", size = 223042 }, + { url = "https://files.pythonhosted.org/packages/2e/61/5673f7e364b31e4e7ef6f61a4b5121c5f170f941895912f773d95270f3a2/contourpy-1.3.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:de39db2604ae755316cb5967728f4bea92685884b1e767b7c24e983ef5f771cb", size = 271630 }, + { url = "https://files.pythonhosted.org/packages/ff/66/a40badddd1223822c95798c55292844b7e871e50f6bfd9f158cb25e0bd39/contourpy-1.3.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3f9e896f447c5c8618f1edb2bafa9a4030f22a575ec418ad70611450720b5b08", size = 255670 }, + { url = "https://files.pythonhosted.org/packages/1e/c7/cf9fdee8200805c9bc3b148f49cb9482a4e3ea2719e772602a425c9b09f8/contourpy-1.3.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71e2bd4a1c4188f5c2b8d274da78faab884b59df20df63c34f74aa1813c4427c", size = 306694 }, + { url = "https://files.pythonhosted.org/packages/dd/e7/ccb9bec80e1ba121efbffad7f38021021cda5be87532ec16fd96533bb2e0/contourpy-1.3.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de425af81b6cea33101ae95ece1f696af39446db9682a0b56daaa48cfc29f38f", size = 345986 }, + { url = "https://files.pythonhosted.org/packages/dc/49/ca13bb2da90391fa4219fdb23b078d6065ada886658ac7818e5441448b78/contourpy-1.3.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:977e98a0e0480d3fe292246417239d2d45435904afd6d7332d8455981c408b85", size = 318060 }, + { url = "https://files.pythonhosted.org/packages/c8/65/5245ce8c548a8422236c13ffcdcdada6a2a812c361e9e0c70548bb40b661/contourpy-1.3.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:434f0adf84911c924519d2b08fc10491dd282b20bdd3fa8f60fd816ea0b48841", size = 322747 }, + { url = "https://files.pythonhosted.org/packages/72/30/669b8eb48e0a01c660ead3752a25b44fdb2e5ebc13a55782f639170772f9/contourpy-1.3.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c66c4906cdbc50e9cba65978823e6e00b45682eb09adbb78c9775b74eb222422", size = 1308895 }, + { url = "https://files.pythonhosted.org/packages/05/5a/b569f4250decee6e8d54498be7bdf29021a4c256e77fe8138c8319ef8eb3/contourpy-1.3.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8b7fc0cd78ba2f4695fd0a6ad81a19e7e3ab825c31b577f384aa9d7817dc3bef", size = 1379098 }, + { url = "https://files.pythonhosted.org/packages/19/ba/b227c3886d120e60e41b28740ac3617b2f2b971b9f601c835661194579f1/contourpy-1.3.2-cp313-cp313-win32.whl", hash = "sha256:15ce6ab60957ca74cff444fe66d9045c1fd3e92c8936894ebd1f3eef2fff075f", size = 178535 }, + { url = "https://files.pythonhosted.org/packages/12/6e/2fed56cd47ca739b43e892707ae9a13790a486a3173be063681ca67d2262/contourpy-1.3.2-cp313-cp313-win_amd64.whl", hash = "sha256:e1578f7eafce927b168752ed7e22646dad6cd9bca673c60bff55889fa236ebf9", size = 223096 }, + { url = "https://files.pythonhosted.org/packages/54/4c/e76fe2a03014a7c767d79ea35c86a747e9325537a8b7627e0e5b3ba266b4/contourpy-1.3.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0475b1f6604896bc7c53bb070e355e9321e1bc0d381735421a2d2068ec56531f", size = 285090 }, + { url = "https://files.pythonhosted.org/packages/7b/e2/5aba47debd55d668e00baf9651b721e7733975dc9fc27264a62b0dd26eb8/contourpy-1.3.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:c85bb486e9be652314bb5b9e2e3b0d1b2e643d5eec4992c0fbe8ac71775da739", size = 268643 }, + { url = "https://files.pythonhosted.org/packages/a1/37/cd45f1f051fe6230f751cc5cdd2728bb3a203f5619510ef11e732109593c/contourpy-1.3.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:745b57db7758f3ffc05a10254edd3182a2a83402a89c00957a8e8a22f5582823", size = 310443 }, + { url = "https://files.pythonhosted.org/packages/8b/a2/36ea6140c306c9ff6dd38e3bcec80b3b018474ef4d17eb68ceecd26675f4/contourpy-1.3.2-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:970e9173dbd7eba9b4e01aab19215a48ee5dd3f43cef736eebde064a171f89a5", size = 349865 }, + { url = "https://files.pythonhosted.org/packages/95/b7/2fc76bc539693180488f7b6cc518da7acbbb9e3b931fd9280504128bf956/contourpy-1.3.2-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c6c4639a9c22230276b7bffb6a850dfc8258a2521305e1faefe804d006b2e532", size = 321162 }, + { url = "https://files.pythonhosted.org/packages/f4/10/76d4f778458b0aa83f96e59d65ece72a060bacb20cfbee46cf6cd5ceba41/contourpy-1.3.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc829960f34ba36aad4302e78eabf3ef16a3a100863f0d4eeddf30e8a485a03b", size = 327355 }, + { url = "https://files.pythonhosted.org/packages/43/a3/10cf483ea683f9f8ab096c24bad3cce20e0d1dd9a4baa0e2093c1c962d9d/contourpy-1.3.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:d32530b534e986374fc19eaa77fcb87e8a99e5431499949b828312bdcd20ac52", size = 1307935 }, + { url = "https://files.pythonhosted.org/packages/78/73/69dd9a024444489e22d86108e7b913f3528f56cfc312b5c5727a44188471/contourpy-1.3.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:e298e7e70cf4eb179cc1077be1c725b5fd131ebc81181bf0c03525c8abc297fd", size = 1372168 }, + { url = "https://files.pythonhosted.org/packages/0f/1b/96d586ccf1b1a9d2004dd519b25fbf104a11589abfd05484ff12199cca21/contourpy-1.3.2-cp313-cp313t-win32.whl", hash = "sha256:d0e589ae0d55204991450bb5c23f571c64fe43adaa53f93fc902a84c96f52fe1", size = 189550 }, + { url = "https://files.pythonhosted.org/packages/b0/e6/6000d0094e8a5e32ad62591c8609e269febb6e4db83a1c75ff8868b42731/contourpy-1.3.2-cp313-cp313t-win_amd64.whl", hash = "sha256:78e9253c3de756b3f6a5174d024c4835acd59eb3f8e2ca13e775dbffe1558f69", size = 238214 }, + { url = "https://files.pythonhosted.org/packages/ff/c0/91f1215d0d9f9f343e4773ba6c9b89e8c0cc7a64a6263f21139da639d848/contourpy-1.3.2-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5f5964cdad279256c084b69c3f412b7801e15356b16efa9d78aa974041903da0", size = 266807 }, + { url = "https://files.pythonhosted.org/packages/d4/79/6be7e90c955c0487e7712660d6cead01fa17bff98e0ea275737cc2bc8e71/contourpy-1.3.2-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49b65a95d642d4efa8f64ba12558fcb83407e58a2dfba9d796d77b63ccfcaff5", size = 318729 }, + { url = "https://files.pythonhosted.org/packages/87/68/7f46fb537958e87427d98a4074bcde4b67a70b04900cfc5ce29bc2f556c1/contourpy-1.3.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:8c5acb8dddb0752bf252e01a3035b21443158910ac16a3b0d20e7fed7d534ce5", size = 221791 }, +] + +[[package]] +name = "cycler" +version = "0.12.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a9/95/a3dbbb5028f35eafb79008e7522a75244477d2838f38cbb722248dabc2a8/cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c", size = 7615 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321 }, +] + +[[package]] +name = "debugpy" +version = "1.8.14" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bd/75/087fe07d40f490a78782ff3b0a30e3968936854105487decdb33446d4b0e/debugpy-1.8.14.tar.gz", hash = "sha256:7cd287184318416850aa8b60ac90105837bb1e59531898c07569d197d2ed5322", size = 1641444 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/67/e8/57fe0c86915671fd6a3d2d8746e40485fd55e8d9e682388fbb3a3d42b86f/debugpy-1.8.14-cp311-cp311-macosx_14_0_universal2.whl", hash = "sha256:1b2ac8c13b2645e0b1eaf30e816404990fbdb168e193322be8f545e8c01644a9", size = 2175064 }, + { url = "https://files.pythonhosted.org/packages/3b/97/2b2fd1b1c9569c6764ccdb650a6f752e4ac31be465049563c9eb127a8487/debugpy-1.8.14-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf431c343a99384ac7eab2f763980724834f933a271e90496944195318c619e2", size = 3132359 }, + { url = "https://files.pythonhosted.org/packages/c0/ee/b825c87ed06256ee2a7ed8bab8fb3bb5851293bf9465409fdffc6261c426/debugpy-1.8.14-cp311-cp311-win32.whl", hash = "sha256:c99295c76161ad8d507b413cd33422d7c542889fbb73035889420ac1fad354f2", size = 5133269 }, + { url = "https://files.pythonhosted.org/packages/d5/a6/6c70cd15afa43d37839d60f324213843174c1d1e6bb616bd89f7c1341bac/debugpy-1.8.14-cp311-cp311-win_amd64.whl", hash = "sha256:7816acea4a46d7e4e50ad8d09d963a680ecc814ae31cdef3622eb05ccacf7b01", size = 5158156 }, + { url = "https://files.pythonhosted.org/packages/d9/2a/ac2df0eda4898f29c46eb6713a5148e6f8b2b389c8ec9e425a4a1d67bf07/debugpy-1.8.14-cp312-cp312-macosx_14_0_universal2.whl", hash = "sha256:8899c17920d089cfa23e6005ad9f22582fd86f144b23acb9feeda59e84405b84", size = 2501268 }, + { url = "https://files.pythonhosted.org/packages/10/53/0a0cb5d79dd9f7039169f8bf94a144ad3efa52cc519940b3b7dde23bcb89/debugpy-1.8.14-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6bb5c0dcf80ad5dbc7b7d6eac484e2af34bdacdf81df09b6a3e62792b722826", size = 4221077 }, + { url = "https://files.pythonhosted.org/packages/f8/d5/84e01821f362327bf4828728aa31e907a2eca7c78cd7c6ec062780d249f8/debugpy-1.8.14-cp312-cp312-win32.whl", hash = "sha256:281d44d248a0e1791ad0eafdbbd2912ff0de9eec48022a5bfbc332957487ed3f", size = 5255127 }, + { url = "https://files.pythonhosted.org/packages/33/16/1ed929d812c758295cac7f9cf3dab5c73439c83d9091f2d91871e648093e/debugpy-1.8.14-cp312-cp312-win_amd64.whl", hash = "sha256:5aa56ef8538893e4502a7d79047fe39b1dae08d9ae257074c6464a7b290b806f", size = 5297249 }, + { url = "https://files.pythonhosted.org/packages/4d/e4/395c792b243f2367d84202dc33689aa3d910fb9826a7491ba20fc9e261f5/debugpy-1.8.14-cp313-cp313-macosx_14_0_universal2.whl", hash = "sha256:329a15d0660ee09fec6786acdb6e0443d595f64f5d096fc3e3ccf09a4259033f", size = 2485676 }, + { url = "https://files.pythonhosted.org/packages/ba/f1/6f2ee3f991327ad9e4c2f8b82611a467052a0fb0e247390192580e89f7ff/debugpy-1.8.14-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f920c7f9af409d90f5fd26e313e119d908b0dd2952c2393cd3247a462331f15", size = 4217514 }, + { url = "https://files.pythonhosted.org/packages/79/28/b9d146f8f2dc535c236ee09ad3e5ac899adb39d7a19b49f03ac95d216beb/debugpy-1.8.14-cp313-cp313-win32.whl", hash = "sha256:3784ec6e8600c66cbdd4ca2726c72d8ca781e94bce2f396cc606d458146f8f4e", size = 5254756 }, + { url = "https://files.pythonhosted.org/packages/e0/62/a7b4a57013eac4ccaef6977966e6bec5c63906dd25a86e35f155952e29a1/debugpy-1.8.14-cp313-cp313-win_amd64.whl", hash = "sha256:684eaf43c95a3ec39a96f1f5195a7ff3d4144e4a18d69bb66beeb1a6de605d6e", size = 5297119 }, + { url = "https://files.pythonhosted.org/packages/97/1a/481f33c37ee3ac8040d3d51fc4c4e4e7e61cb08b8bc8971d6032acc2279f/debugpy-1.8.14-py2.py3-none-any.whl", hash = "sha256:5cd9a579d553b6cb9759a7908a41988ee6280b961f24f63336835d9418216a20", size = 5256230 }, +] + +[[package]] +name = "decorator" +version = "5.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/fa/6d96a0978d19e17b68d634497769987b16c8f4cd0a7a05048bec693caa6b/decorator-5.2.1.tar.gz", hash = "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360", size = 56711 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a", size = 9190 }, +] + +[[package]] +name = "docker-pycreds" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c5/e6/d1f6c00b7221e2d7c4b470132c931325c8b22c51ca62417e300f5ce16009/docker-pycreds-0.4.0.tar.gz", hash = "sha256:6ce3270bcaf404cc4c3e27e4b6c70d3521deae82fb508767870fdbf772d584d4", size = 8754 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f5/e8/f6bd1eee09314e7e6dee49cbe2c5e22314ccdb38db16c9fc72d2fa80d054/docker_pycreds-0.4.0-py2.py3-none-any.whl", hash = "sha256:7266112468627868005106ec19cd0d722702d2b7d5912a28e19b826c3d37af49", size = 8982 }, +] + +[[package]] +name = "ecos" +version = "2.0.14" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, + { name = "scipy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2e/5f/17716c533da95ed110815b159efa22b1064c8c41fd5c862f21aff7a7fec0/ecos-2.0.14.tar.gz", hash = "sha256:64b3201c0e0a7f0129050557c4ac50b00031e80a10534506dba1200c8dc1efe4", size = 142430 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2b/9b/c886a268d4b7adfaa1171244cdbfa3c944e5a599fe7a5e738ee27390ab20/ecos-2.0.14-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:dc90b54eaae16ead128bfdd95e04bf808b73578bdf40ed652c55aa36a6d02e42", size = 92594 }, + { url = "https://files.pythonhosted.org/packages/49/e9/fae34e8ef6a9b78c3098a4428ed0e8f77cdeb334a7dc17c649abb686ed08/ecos-2.0.14-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a8be3b4856838ae351fec40fb3589181d52b41cf75bf4d35342686a508c37a6", size = 220084 }, + { url = "https://files.pythonhosted.org/packages/2f/45/1e52519d6c29dd26bbfaf92ece5b45ca3de3b7c8b2615a818aaeadb7ad63/ecos-2.0.14-cp311-cp311-win_amd64.whl", hash = "sha256:7495b3031ccc2d4cec72cdb40aed8a2d1fdd734fe40519b7e6047aead5e811cf", size = 72199 }, + { url = "https://files.pythonhosted.org/packages/af/c3/84e392f2410f51fa557198937cc52a2e80f887c517ef4e3fb6d46e3bb008/ecos-2.0.14-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:4a7e2704a3ef9acfb8146d594deff9942d3a0f0d0399de8fe2e0bd95e8b0855c", size = 92545 }, + { url = "https://files.pythonhosted.org/packages/82/12/42f4d953f9284571726b085f99e13bfa84522bf63bf2e7a81460013b09e6/ecos-2.0.14-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3cbb1a66ecf10955a1a4bcd6b99db55148000cb79fd176bfac26d98b21a4814", size = 222132 }, + { url = "https://files.pythonhosted.org/packages/56/9a/ca30572f3e3ff3cef6a0ea8aa6cdc12c36f9fefe559f65c7d6265713196a/ecos-2.0.14-cp312-cp312-win_amd64.whl", hash = "sha256:718eb62afb8e45426bcc365ebaf3ca9f610afcbb754de6073ef5f104da8fca1f", size = 72248 }, +] + +[[package]] +name = "einops" +version = "0.8.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e5/81/df4fbe24dff8ba3934af99044188e20a98ed441ad17a274539b74e82e126/einops-0.8.1.tar.gz", hash = "sha256:de5d960a7a761225532e0f1959e5315ebeafc0cd43394732f103ca44b9837e84", size = 54805 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/87/62/9773de14fe6c45c23649e98b83231fffd7b9892b6cf863251dc2afa73643/einops-0.8.1-py3-none-any.whl", hash = "sha256:919387eb55330f5757c6bea9165c5ff5cfe63a642682ea788a6d472576d81737", size = 64359 }, +] + +[[package]] +name = "einops-exts" +version = "0.0.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "einops" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3e/cc/f2b03b24894faaaa425cf46508e6b1f449d908841f61aeb7c14f0b18a3f3/einops-exts-0.0.4.tar.gz", hash = "sha256:616f145b3411f8e9e3be5da5c968bbe372e55c249de11faa909c7a4b74580a6c", size = 3548 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/01/8da8dd078b354a89602a875d310a0d725dad92b5b4d61069576e0a0e02e4/einops_exts-0.0.4-py3-none-any.whl", hash = "sha256:6d310a4c858e459ebff8288580f90255d354cfa3bde22a53b59baae64b48cb95", size = 3925 }, +] + +[[package]] +name = "environs" +version = "11.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "marshmallow" }, + { name = "python-dotenv" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f2/13/3d448cfbed9f1baff5765f49434cd849501351f14fd3f09f0f2e9bd35322/environs-11.0.0.tar.gz", hash = "sha256:069727a8f73d8ba8d033d3cd95c0da231d44f38f1da773bf076cef168d312ee8", size = 25787 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3f/30/ef8a3022e6cdcedfd7ba03ca88ab29e30334f8e958cdbf5ce120912397e8/environs-11.0.0-py3-none-any.whl", hash = "sha256:e0bcfd41c718c07a7db422f9109e490746450da38793fe4ee197f397b9343435", size = 12216 }, +] + +[[package]] +name = "et-xmlfile" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d3/38/af70d7ab1ae9d4da450eeec1fa3918940a5fafb9055e934af8d6eb0c2313/et_xmlfile-2.0.0.tar.gz", hash = "sha256:dab3f4764309081ce75662649be815c4c9081e88f0837825f90fd28317d4da54", size = 17234 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c1/8b/5fe2cc11fee489817272089c4203e679c63b570a5aaeb18d852ae3cbba6a/et_xmlfile-2.0.0-py3-none-any.whl", hash = "sha256:7a91720bc756843502c3b7504c77b8fe44217c85c537d85037f0f536151b2caa", size = 18059 }, +] + +[[package]] +name = "executing" +version = "2.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/91/50/a9d80c47ff289c611ff12e63f7c5d13942c65d68125160cefd768c73e6e4/executing-2.2.0.tar.gz", hash = "sha256:5d108c028108fe2551d1a7b2e8b713341e2cb4fc0aa7dcf966fa4327a5226755", size = 978693 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7b/8f/c4d9bafc34ad7ad5d8dc16dd1347ee0e507a52c3adb6bfa8887e1c6a26ba/executing-2.2.0-py2.py3-none-any.whl", hash = "sha256:11387150cad388d62750327a53d3339fad4888b39a6fe233c3afbb54ecffd3aa", size = 26702 }, +] + +[[package]] +name = "fairscale" +version = "0.4.13" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, + { name = "torch" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c1/08/b3334d7b543ac10dcb129cef4f84723ab696725512f18d69ab3a784b0bf5/fairscale-0.4.13.tar.gz", hash = "sha256:1b797825c427f5dba92253fd0d8daa574e8bd651a2423497775fab1b30cfb768", size = 266261 } + +[[package]] +name = "filelock" +version = "3.18.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0a/10/c23352565a6544bdc5353e0b15fc1c563352101f30e24bf500207a54df9a/filelock-3.18.0.tar.gz", hash = "sha256:adbc88eabb99d2fec8c9c1b229b171f18afa655400173ddc653d5d01501fb9f2", size = 18075 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4d/36/2a115987e2d8c300a974597416d9de88f2444426de9571f4b59b2cca3acc/filelock-3.18.0-py3-none-any.whl", hash = "sha256:c401f4f8377c4464e6db25fff06205fd89bdd83b65eb0488ed1b160f780e21de", size = 16215 }, +] + +[[package]] +name = "flash-attn" +version = "2.8.0.post2" +source = { git = "https://github.com/KatherLab/flash-attention.git?rev=7593c84a0d36b7f2ead10660209b6f8b374ade4e#7593c84a0d36b7f2ead10660209b6f8b374ade4e" } +dependencies = [ + { name = "einops" }, + { name = "torch" }, +] + +[[package]] +name = "fonttools" +version = "4.58.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3e/7a/30c581aeaa86d94e7a29344bccefd2408870bf5b0e7640b6f4ffede61bd0/fonttools-4.58.1.tar.gz", hash = "sha256:cbc8868e0a29c3e22628dfa1432adf7a104d86d1bc661cecc3e9173070b6ab2d", size = 3519505 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/50/3f/9fecd69149b0eec5ca46ec58de83b2fd34d07204fe2c12c209255082507a/fonttools-4.58.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9966e14729669bcfbb56f83b747a2397c4d97c6d4798cb2e2adc28f9388fa008", size = 2754713 }, + { url = "https://files.pythonhosted.org/packages/c8/19/d04ea5f3ab2afa7799f2b1ebe1d57ff71b479f99f29b82bddc7197d50220/fonttools-4.58.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:64cc1647bbe83dea57f5496ec878ad19ccdba7185b0dd34955d3e6f03dc789e6", size = 2316637 }, + { url = "https://files.pythonhosted.org/packages/5c/3f/375f59d756b17318336c050363849011e03ac82904538f39ebe8189835bc/fonttools-4.58.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:464f790ce681d08d1583df0735776aa9cb1999594bf336ddd0bf962c17b629ac", size = 4915730 }, + { url = "https://files.pythonhosted.org/packages/2f/90/069f859d6f6480503574cda21b84ceee98bf5f5fd1764f26674e828a2600/fonttools-4.58.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c53c6a720ee70cc25746d511ba88c45c95ec510fd258026ed209b0b9e3ba92f", size = 4936194 }, + { url = "https://files.pythonhosted.org/packages/01/11/339973e588e1c27f20c578f845bdcf84376c5e42bd35fca05419fd8d1648/fonttools-4.58.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b6823a633bbce29cf3033508ebb54a433c473fb9833eff7f936bfdc5204fd98d", size = 4978982 }, + { url = "https://files.pythonhosted.org/packages/a7/aa/1c627532a69715f54b8d96ab3a7bc8628f6e89989e9275dfc067dc2d6d56/fonttools-4.58.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5701fe66a1408c1974d2f78c00f964f8aad17cccbc32bc041e1b81421f31f448", size = 5090087 }, + { url = "https://files.pythonhosted.org/packages/77/ce/cf7b624db35bce589ac1f2c98329ea91b28f0283d3b7e9e6126dfaeb5abd/fonttools-4.58.1-cp311-cp311-win32.whl", hash = "sha256:4cad2c74adf9ee31ae43be6b0b376fdb386d4d50c60979790e32c3548efec051", size = 2188923 }, + { url = "https://files.pythonhosted.org/packages/b9/22/c4f1f76eeb1b9353e9cc81451d0ae08acc3d3aa31b9ab8f3791a18af1f89/fonttools-4.58.1-cp311-cp311-win_amd64.whl", hash = "sha256:7ade12485abccb0f6b6a6e2a88c50e587ff0e201e48e0153dd9b2e0ed67a2f38", size = 2236853 }, + { url = "https://files.pythonhosted.org/packages/32/97/ed1078b1e138fbc0b4ee75878000d549a70c02d83bb4e557e416efc34140/fonttools-4.58.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:f56085a65769dc0100822c814069327541db9c3c4f21e599c6138f9dbda75e96", size = 2740473 }, + { url = "https://files.pythonhosted.org/packages/28/35/53d49fb7d6b30128153d11628b976fda3ce8ae44234b5a81c4edb3023798/fonttools-4.58.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:19c65a88e522c9f1be0c05d73541de20feada99d23d06e9b5354023cc3e517b0", size = 2309936 }, + { url = "https://files.pythonhosted.org/packages/0c/db/8b63c1d673b2bf0cfed77500d47769dc4aa85453b5f0ef525db2cf952895/fonttools-4.58.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b01bb37006e97703300bfde7a73d1c7038574dd1df9d8d92ca99af151becf2ca", size = 4814671 }, + { url = "https://files.pythonhosted.org/packages/a6/13/0b96eeb148b77c521b8e94628c59d15e4fb0e76191c41f5616a656d6adb9/fonttools-4.58.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d629dea240f0fc826d8bb14566e95c663214eece21b5932c9228d3e8907f55aa", size = 4881493 }, + { url = "https://files.pythonhosted.org/packages/ac/b0/9f8aa60e8e5be91aba8dfaa3fa6b33fd950511686921cf27e97bf4154e3d/fonttools-4.58.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ef0b33ff35421a04a638e736823c2dee9d200cdd275cfdb43e875ca745150aae", size = 4874960 }, + { url = "https://files.pythonhosted.org/packages/b6/7e/83b409659eb4818f1283a8319f3570497718d6d3b70f4fca2ddf962e948e/fonttools-4.58.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4db9399ee633855c718fe8bea5eecbdc5bf3fdbed2648e50f67f8946b943ed1c", size = 5026677 }, + { url = "https://files.pythonhosted.org/packages/34/52/1eb69802d3b54e569158c97810195f317d350f56390b83c43e1c999551d8/fonttools-4.58.1-cp312-cp312-win32.whl", hash = "sha256:5cf04c4f73d36b30ea1cff091a7a9e65f8d5b08345b950f82679034e9f7573f4", size = 2176201 }, + { url = "https://files.pythonhosted.org/packages/6f/25/8dcfeb771de8d9cdffab2b957a05af4395d41ec9a198ec139d2326366a07/fonttools-4.58.1-cp312-cp312-win_amd64.whl", hash = "sha256:4a3841b59c67fa1f739542b05211609c453cec5d11d21f863dd2652d5a81ec9b", size = 2225519 }, + { url = "https://files.pythonhosted.org/packages/83/7a/7ed2e4e381f9b1f5122d33b7e626a40f646cacc1ef72d8806aacece9e580/fonttools-4.58.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:68379d1599fc59569956a97eb7b07e0413f76142ac8513fa24c9f2c03970543a", size = 2731231 }, + { url = "https://files.pythonhosted.org/packages/e7/28/74864dc9248e917cbe07c903e0ce1517c89d42e2fab6b0ce218387ef0e24/fonttools-4.58.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8631905657de4f9a7ae1e12186c1ed20ba4d6168c2d593b9e0bd2908061d341b", size = 2305224 }, + { url = "https://files.pythonhosted.org/packages/e7/f1/ced758896188c1632c5b034a0741457f305e087eb4fa762d86aa3c1ae422/fonttools-4.58.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2ecea7289061c2c71468723409a8dd6e70d1ecfce6bc7686e5a74b9ce9154fe", size = 4793934 }, + { url = "https://files.pythonhosted.org/packages/c1/46/8b46469c6edac393de1c380c7ec61922d5440f25605dfca7849e5ffff295/fonttools-4.58.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9b8860f8cd48b345bd1df1d7be650f600f69ee971ffe338c5bd5bcb6bdb3b92c", size = 4863415 }, + { url = "https://files.pythonhosted.org/packages/12/1b/82aa678bb96af6663fe163d51493ffb8622948f4908c886cba6b67fbf6c5/fonttools-4.58.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7c9a0acdefcb8d7ccd7c59202056166c400e797047009ecb299b75ab950c2a9c", size = 4865025 }, + { url = "https://files.pythonhosted.org/packages/7d/26/b66ab2f2dc34b962caecd6fa72a036395b1bc9fb849f52856b1e1144cd63/fonttools-4.58.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1e1fac0be6be3e4309058e156948cb73196e5fd994268b89b5e3f5a26ee2b582", size = 5002698 }, + { url = "https://files.pythonhosted.org/packages/7b/56/cdddc63333ed77e810df56e5e7fb93659022d535a670335d8792be6d59fd/fonttools-4.58.1-cp313-cp313-win32.whl", hash = "sha256:aed7f93a9a072f0ce6fb46aad9474824ac6dd9c7c38a72f8295dd14f2215950f", size = 2174515 }, + { url = "https://files.pythonhosted.org/packages/ba/81/c7f395718e44cebe1010fcd7f1b91957d65d512d5f03114d2d6d00cae1c4/fonttools-4.58.1-cp313-cp313-win_amd64.whl", hash = "sha256:b27d69c97c20c9bca807f7ae7fc7df459eb62994859ff6a2a489e420634deac3", size = 2225290 }, + { url = "https://files.pythonhosted.org/packages/21/ff/995277586691c0cc314c28b24b4ec30610440fd7bf580072aed1409f95b0/fonttools-4.58.1-py3-none-any.whl", hash = "sha256:db88365d0962cd6f5bce54b190a4669aeed9c9941aa7bd60a5af084d8d9173d6", size = 1113429 }, +] + +[[package]] +name = "formulaic" +version = "1.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "interface-meta" }, + { name = "numpy" }, + { name = "pandas" }, + { name = "scipy" }, + { name = "typing-extensions" }, + { name = "wrapt" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5c/30/03b5e3bb62374db3f665ca3020fdfc4304e98ceeaaa9dcd7a47a6b574ebf/formulaic-1.1.1.tar.gz", hash = "sha256:ddf80e4bef976dd99698aa27512015276c7b86c314b601ae6fd360c7741b7231", size = 652602 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d2/c2/a34097e53efe70a538ae97574ff9e9866e60fc1c792c19da5fd6b56ce7b5/formulaic-1.1.1-py3-none-any.whl", hash = "sha256:bbb7e38f99e4bcdc62cb0a6a818ad33b370b4e98e9e4f0b276561448482c8268", size = 115718 }, +] + +[[package]] +name = "frozenlist" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/f4/d744cba2da59b5c1d88823cf9e8a6c74e4659e2b27604ed973be2a0bf5ab/frozenlist-1.6.0.tar.gz", hash = "sha256:b99655c32c1c8e06d111e7f41c06c29a5318cb1835df23a45518e02a47c63b68", size = 42831 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/53/b5/bc883b5296ec902115c00be161da93bf661199c465ec4c483feec6ea4c32/frozenlist-1.6.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ae8337990e7a45683548ffb2fee1af2f1ed08169284cd829cdd9a7fa7470530d", size = 160912 }, + { url = "https://files.pythonhosted.org/packages/6f/93/51b058b563d0704b39c56baa222828043aafcac17fd3734bec5dbeb619b1/frozenlist-1.6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8c952f69dd524558694818a461855f35d36cc7f5c0adddce37e962c85d06eac0", size = 124315 }, + { url = "https://files.pythonhosted.org/packages/c9/e0/46cd35219428d350558b874d595e132d1c17a9471a1bd0d01d518a261e7c/frozenlist-1.6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8f5fef13136c4e2dee91bfb9a44e236fff78fc2cd9f838eddfc470c3d7d90afe", size = 122230 }, + { url = "https://files.pythonhosted.org/packages/d1/0f/7ad2ce928ad06d6dd26a61812b959ded573d3e9d0ee6109d96c2be7172e9/frozenlist-1.6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:716bbba09611b4663ecbb7cd022f640759af8259e12a6ca939c0a6acd49eedba", size = 314842 }, + { url = "https://files.pythonhosted.org/packages/34/76/98cbbd8a20a5c3359a2004ae5e5b216af84a150ccbad67c8f8f30fb2ea91/frozenlist-1.6.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7b8c4dc422c1a3ffc550b465090e53b0bf4839047f3e436a34172ac67c45d595", size = 304919 }, + { url = "https://files.pythonhosted.org/packages/9a/fa/258e771ce3a44348c05e6b01dffc2bc67603fba95761458c238cd09a2c77/frozenlist-1.6.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b11534872256e1666116f6587a1592ef395a98b54476addb5e8d352925cb5d4a", size = 324074 }, + { url = "https://files.pythonhosted.org/packages/d5/a4/047d861fd8c538210e12b208c0479912273f991356b6bdee7ea8356b07c9/frozenlist-1.6.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c6eceb88aaf7221f75be6ab498dc622a151f5f88d536661af3ffc486245a626", size = 321292 }, + { url = "https://files.pythonhosted.org/packages/c0/25/cfec8af758b4525676cabd36efcaf7102c1348a776c0d1ad046b8a7cdc65/frozenlist-1.6.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62c828a5b195570eb4b37369fcbbd58e96c905768d53a44d13044355647838ff", size = 301569 }, + { url = "https://files.pythonhosted.org/packages/87/2f/0c819372fa9f0c07b153124bf58683b8d0ca7bb73ea5ccde9b9ef1745beb/frozenlist-1.6.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1c6bd2c6399920c9622362ce95a7d74e7f9af9bfec05fff91b8ce4b9647845a", size = 313625 }, + { url = "https://files.pythonhosted.org/packages/50/5f/f0cf8b0fdedffdb76b3745aa13d5dbe404d63493cc211ce8250f2025307f/frozenlist-1.6.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:49ba23817781e22fcbd45fd9ff2b9b8cdb7b16a42a4851ab8025cae7b22e96d0", size = 312523 }, + { url = "https://files.pythonhosted.org/packages/e1/6c/38c49108491272d3e84125bbabf2c2d0b304899b52f49f0539deb26ad18d/frozenlist-1.6.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:431ef6937ae0f853143e2ca67d6da76c083e8b1fe3df0e96f3802fd37626e606", size = 322657 }, + { url = "https://files.pythonhosted.org/packages/bd/4b/3bd3bad5be06a9d1b04b1c22be80b5fe65b502992d62fab4bdb25d9366ee/frozenlist-1.6.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:9d124b38b3c299ca68433597ee26b7819209cb8a3a9ea761dfe9db3a04bba584", size = 303414 }, + { url = "https://files.pythonhosted.org/packages/5b/89/7e225a30bef6e85dbfe22622c24afe932e9444de3b40d58b1ea589a14ef8/frozenlist-1.6.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:118e97556306402e2b010da1ef21ea70cb6d6122e580da64c056b96f524fbd6a", size = 320321 }, + { url = "https://files.pythonhosted.org/packages/22/72/7e3acef4dd9e86366cb8f4d8f28e852c2b7e116927e9722b31a6f71ea4b0/frozenlist-1.6.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:fb3b309f1d4086b5533cf7bbcf3f956f0ae6469664522f1bde4feed26fba60f1", size = 323975 }, + { url = "https://files.pythonhosted.org/packages/d8/85/e5da03d20507e13c66ce612c9792b76811b7a43e3320cce42d95b85ac755/frozenlist-1.6.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:54dece0d21dce4fdb188a1ffc555926adf1d1c516e493c2914d7c370e454bc9e", size = 316553 }, + { url = "https://files.pythonhosted.org/packages/ac/8e/6c609cbd0580ae8a0661c408149f196aade7d325b1ae7adc930501b81acb/frozenlist-1.6.0-cp311-cp311-win32.whl", hash = "sha256:654e4ba1d0b2154ca2f096bed27461cf6160bc7f504a7f9a9ef447c293caf860", size = 115511 }, + { url = "https://files.pythonhosted.org/packages/f2/13/a84804cfde6de12d44ed48ecbf777ba62b12ff09e761f76cdd1ff9e14bb1/frozenlist-1.6.0-cp311-cp311-win_amd64.whl", hash = "sha256:3e911391bffdb806001002c1f860787542f45916c3baf764264a52765d5a5603", size = 120863 }, + { url = "https://files.pythonhosted.org/packages/9c/8a/289b7d0de2fbac832ea80944d809759976f661557a38bb8e77db5d9f79b7/frozenlist-1.6.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:c5b9e42ace7d95bf41e19b87cec8f262c41d3510d8ad7514ab3862ea2197bfb1", size = 160193 }, + { url = "https://files.pythonhosted.org/packages/19/80/2fd17d322aec7f430549f0669f599997174f93ee17929ea5b92781ec902c/frozenlist-1.6.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ca9973735ce9f770d24d5484dcb42f68f135351c2fc81a7a9369e48cf2998a29", size = 123831 }, + { url = "https://files.pythonhosted.org/packages/99/06/f5812da431273f78c6543e0b2f7de67dfd65eb0a433978b2c9c63d2205e4/frozenlist-1.6.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6ac40ec76041c67b928ca8aaffba15c2b2ee3f5ae8d0cb0617b5e63ec119ca25", size = 121862 }, + { url = "https://files.pythonhosted.org/packages/d0/31/9e61c6b5fc493cf24d54881731204d27105234d09878be1a5983182cc4a5/frozenlist-1.6.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:95b7a8a3180dfb280eb044fdec562f9b461614c0ef21669aea6f1d3dac6ee576", size = 316361 }, + { url = "https://files.pythonhosted.org/packages/9d/55/22ca9362d4f0222324981470fd50192be200154d51509ee6eb9baa148e96/frozenlist-1.6.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c444d824e22da6c9291886d80c7d00c444981a72686e2b59d38b285617cb52c8", size = 307115 }, + { url = "https://files.pythonhosted.org/packages/ae/39/4fff42920a57794881e7bb3898dc7f5f539261711ea411b43bba3cde8b79/frozenlist-1.6.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bb52c8166499a8150bfd38478248572c924c003cbb45fe3bcd348e5ac7c000f9", size = 322505 }, + { url = "https://files.pythonhosted.org/packages/55/f2/88c41f374c1e4cf0092a5459e5f3d6a1e17ed274c98087a76487783df90c/frozenlist-1.6.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b35298b2db9c2468106278537ee529719228950a5fdda686582f68f247d1dc6e", size = 322666 }, + { url = "https://files.pythonhosted.org/packages/75/51/034eeb75afdf3fd03997856195b500722c0b1a50716664cde64e28299c4b/frozenlist-1.6.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d108e2d070034f9d57210f22fefd22ea0d04609fc97c5f7f5a686b3471028590", size = 302119 }, + { url = "https://files.pythonhosted.org/packages/2b/a6/564ecde55ee633270a793999ef4fd1d2c2b32b5a7eec903b1012cb7c5143/frozenlist-1.6.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e1be9111cb6756868ac242b3c2bd1f09d9aea09846e4f5c23715e7afb647103", size = 316226 }, + { url = "https://files.pythonhosted.org/packages/f1/c8/6c0682c32377f402b8a6174fb16378b683cf6379ab4d2827c580892ab3c7/frozenlist-1.6.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:94bb451c664415f02f07eef4ece976a2c65dcbab9c2f1705b7031a3a75349d8c", size = 312788 }, + { url = "https://files.pythonhosted.org/packages/b6/b8/10fbec38f82c5d163ca1750bfff4ede69713badf236a016781cf1f10a0f0/frozenlist-1.6.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:d1a686d0b0949182b8faddea596f3fc11f44768d1f74d4cad70213b2e139d821", size = 325914 }, + { url = "https://files.pythonhosted.org/packages/62/ca/2bf4f3a1bd40cdedd301e6ecfdbb291080d5afc5f9ce350c0739f773d6b9/frozenlist-1.6.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:ea8e59105d802c5a38bdbe7362822c522230b3faba2aa35c0fa1765239b7dd70", size = 305283 }, + { url = "https://files.pythonhosted.org/packages/09/64/20cc13ccf94abc2a1f482f74ad210703dc78a590d0b805af1c9aa67f76f9/frozenlist-1.6.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:abc4e880a9b920bc5020bf6a431a6bb40589d9bca3975c980495f63632e8382f", size = 319264 }, + { url = "https://files.pythonhosted.org/packages/20/ff/86c6a2bbe98cfc231519f5e6d712a0898488ceac804a917ce014f32e68f6/frozenlist-1.6.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9a79713adfe28830f27a3c62f6b5406c37376c892b05ae070906f07ae4487046", size = 326482 }, + { url = "https://files.pythonhosted.org/packages/2f/da/8e381f66367d79adca245d1d71527aac774e30e291d41ef161ce2d80c38e/frozenlist-1.6.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9a0318c2068e217a8f5e3b85e35899f5a19e97141a45bb925bb357cfe1daf770", size = 318248 }, + { url = "https://files.pythonhosted.org/packages/39/24/1a1976563fb476ab6f0fa9fefaac7616a4361dbe0461324f9fd7bf425dbe/frozenlist-1.6.0-cp312-cp312-win32.whl", hash = "sha256:853ac025092a24bb3bf09ae87f9127de9fe6e0c345614ac92536577cf956dfcc", size = 115161 }, + { url = "https://files.pythonhosted.org/packages/80/2e/fb4ed62a65f8cd66044706b1013f0010930d8cbb0729a2219561ea075434/frozenlist-1.6.0-cp312-cp312-win_amd64.whl", hash = "sha256:2bdfe2d7e6c9281c6e55523acd6c2bf77963cb422fdc7d142fb0cb6621b66878", size = 120548 }, + { url = "https://files.pythonhosted.org/packages/6f/e5/04c7090c514d96ca00887932417f04343ab94904a56ab7f57861bf63652d/frozenlist-1.6.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:1d7fb014fe0fbfee3efd6a94fc635aeaa68e5e1720fe9e57357f2e2c6e1a647e", size = 158182 }, + { url = "https://files.pythonhosted.org/packages/e9/8f/60d0555c61eec855783a6356268314d204137f5e0c53b59ae2fc28938c99/frozenlist-1.6.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:01bcaa305a0fdad12745502bfd16a1c75b14558dabae226852f9159364573117", size = 122838 }, + { url = "https://files.pythonhosted.org/packages/5a/a7/d0ec890e3665b4b3b7c05dc80e477ed8dc2e2e77719368e78e2cd9fec9c8/frozenlist-1.6.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8b314faa3051a6d45da196a2c495e922f987dc848e967d8cfeaee8a0328b1cd4", size = 120980 }, + { url = "https://files.pythonhosted.org/packages/cc/19/9b355a5e7a8eba903a008579964192c3e427444752f20b2144b10bb336df/frozenlist-1.6.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da62fecac21a3ee10463d153549d8db87549a5e77eefb8c91ac84bb42bb1e4e3", size = 305463 }, + { url = "https://files.pythonhosted.org/packages/9c/8d/5b4c758c2550131d66935ef2fa700ada2461c08866aef4229ae1554b93ca/frozenlist-1.6.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d1eb89bf3454e2132e046f9599fbcf0a4483ed43b40f545551a39316d0201cd1", size = 297985 }, + { url = "https://files.pythonhosted.org/packages/48/2c/537ec09e032b5865715726b2d1d9813e6589b571d34d01550c7aeaad7e53/frozenlist-1.6.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d18689b40cb3936acd971f663ccb8e2589c45db5e2c5f07e0ec6207664029a9c", size = 311188 }, + { url = "https://files.pythonhosted.org/packages/31/2f/1aa74b33f74d54817055de9a4961eff798f066cdc6f67591905d4fc82a84/frozenlist-1.6.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e67ddb0749ed066b1a03fba812e2dcae791dd50e5da03be50b6a14d0c1a9ee45", size = 311874 }, + { url = "https://files.pythonhosted.org/packages/bf/f0/cfec18838f13ebf4b37cfebc8649db5ea71a1b25dacd691444a10729776c/frozenlist-1.6.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fc5e64626e6682638d6e44398c9baf1d6ce6bc236d40b4b57255c9d3f9761f1f", size = 291897 }, + { url = "https://files.pythonhosted.org/packages/ea/a5/deb39325cbbea6cd0a46db8ccd76150ae2fcbe60d63243d9df4a0b8c3205/frozenlist-1.6.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:437cfd39564744ae32ad5929e55b18ebd88817f9180e4cc05e7d53b75f79ce85", size = 305799 }, + { url = "https://files.pythonhosted.org/packages/78/22/6ddec55c5243a59f605e4280f10cee8c95a449f81e40117163383829c241/frozenlist-1.6.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:62dd7df78e74d924952e2feb7357d826af8d2f307557a779d14ddf94d7311be8", size = 302804 }, + { url = "https://files.pythonhosted.org/packages/5d/b7/d9ca9bab87f28855063c4d202936800219e39db9e46f9fb004d521152623/frozenlist-1.6.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:a66781d7e4cddcbbcfd64de3d41a61d6bdde370fc2e38623f30b2bd539e84a9f", size = 316404 }, + { url = "https://files.pythonhosted.org/packages/a6/3a/1255305db7874d0b9eddb4fe4a27469e1fb63720f1fc6d325a5118492d18/frozenlist-1.6.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:482fe06e9a3fffbcd41950f9d890034b4a54395c60b5e61fae875d37a699813f", size = 295572 }, + { url = "https://files.pythonhosted.org/packages/2a/f2/8d38eeee39a0e3a91b75867cc102159ecccf441deb6ddf67be96d3410b84/frozenlist-1.6.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:e4f9373c500dfc02feea39f7a56e4f543e670212102cc2eeb51d3a99c7ffbde6", size = 307601 }, + { url = "https://files.pythonhosted.org/packages/38/04/80ec8e6b92f61ef085422d7b196822820404f940950dde5b2e367bede8bc/frozenlist-1.6.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:e69bb81de06827147b7bfbaeb284d85219fa92d9f097e32cc73675f279d70188", size = 314232 }, + { url = "https://files.pythonhosted.org/packages/3a/58/93b41fb23e75f38f453ae92a2f987274c64637c450285577bd81c599b715/frozenlist-1.6.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7613d9977d2ab4a9141dde4a149f4357e4065949674c5649f920fec86ecb393e", size = 308187 }, + { url = "https://files.pythonhosted.org/packages/6a/a2/e64df5c5aa36ab3dee5a40d254f3e471bb0603c225f81664267281c46a2d/frozenlist-1.6.0-cp313-cp313-win32.whl", hash = "sha256:4def87ef6d90429f777c9d9de3961679abf938cb6b7b63d4a7eb8a268babfce4", size = 114772 }, + { url = "https://files.pythonhosted.org/packages/a0/77/fead27441e749b2d574bb73d693530d59d520d4b9e9679b8e3cb779d37f2/frozenlist-1.6.0-cp313-cp313-win_amd64.whl", hash = "sha256:37a8a52c3dfff01515e9bbbee0e6063181362f9de3db2ccf9bc96189b557cbfd", size = 119847 }, + { url = "https://files.pythonhosted.org/packages/df/bd/cc6d934991c1e5d9cafda83dfdc52f987c7b28343686aef2e58a9cf89f20/frozenlist-1.6.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:46138f5a0773d064ff663d273b309b696293d7a7c00a0994c5c13a5078134b64", size = 174937 }, + { url = "https://files.pythonhosted.org/packages/f2/a2/daf945f335abdbfdd5993e9dc348ef4507436936ab3c26d7cfe72f4843bf/frozenlist-1.6.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:f88bc0a2b9c2a835cb888b32246c27cdab5740059fb3688852bf91e915399b91", size = 136029 }, + { url = "https://files.pythonhosted.org/packages/51/65/4c3145f237a31247c3429e1c94c384d053f69b52110a0d04bfc8afc55fb2/frozenlist-1.6.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:777704c1d7655b802c7850255639672e90e81ad6fa42b99ce5ed3fbf45e338dd", size = 134831 }, + { url = "https://files.pythonhosted.org/packages/77/38/03d316507d8dea84dfb99bdd515ea245628af964b2bf57759e3c9205cc5e/frozenlist-1.6.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85ef8d41764c7de0dcdaf64f733a27352248493a85a80661f3c678acd27e31f2", size = 392981 }, + { url = "https://files.pythonhosted.org/packages/37/02/46285ef9828f318ba400a51d5bb616ded38db8466836a9cfa39f3903260b/frozenlist-1.6.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:da5cb36623f2b846fb25009d9d9215322318ff1c63403075f812b3b2876c8506", size = 371999 }, + { url = "https://files.pythonhosted.org/packages/0d/64/1212fea37a112c3c5c05bfb5f0a81af4836ce349e69be75af93f99644da9/frozenlist-1.6.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cbb56587a16cf0fb8acd19e90ff9924979ac1431baea8681712716a8337577b0", size = 392200 }, + { url = "https://files.pythonhosted.org/packages/81/ce/9a6ea1763e3366e44a5208f76bf37c76c5da570772375e4d0be85180e588/frozenlist-1.6.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c6154c3ba59cda3f954c6333025369e42c3acd0c6e8b6ce31eb5c5b8116c07e0", size = 390134 }, + { url = "https://files.pythonhosted.org/packages/bc/36/939738b0b495b2c6d0c39ba51563e453232813042a8d908b8f9544296c29/frozenlist-1.6.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e8246877afa3f1ae5c979fe85f567d220f86a50dc6c493b9b7d8191181ae01e", size = 365208 }, + { url = "https://files.pythonhosted.org/packages/b4/8b/939e62e93c63409949c25220d1ba8e88e3960f8ef6a8d9ede8f94b459d27/frozenlist-1.6.0-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b0f6cce16306d2e117cf9db71ab3a9e8878a28176aeaf0dbe35248d97b28d0c", size = 385548 }, + { url = "https://files.pythonhosted.org/packages/62/38/22d2873c90102e06a7c5a3a5b82ca47e393c6079413e8a75c72bff067fa8/frozenlist-1.6.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:1b8e8cd8032ba266f91136d7105706ad57770f3522eac4a111d77ac126a25a9b", size = 391123 }, + { url = "https://files.pythonhosted.org/packages/44/78/63aaaf533ee0701549500f6d819be092c6065cb5c577edb70c09df74d5d0/frozenlist-1.6.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:e2ada1d8515d3ea5378c018a5f6d14b4994d4036591a52ceaf1a1549dec8e1ad", size = 394199 }, + { url = "https://files.pythonhosted.org/packages/54/45/71a6b48981d429e8fbcc08454dc99c4c2639865a646d549812883e9c9dd3/frozenlist-1.6.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:cdb2c7f071e4026c19a3e32b93a09e59b12000751fc9b0b7758da899e657d215", size = 373854 }, + { url = "https://files.pythonhosted.org/packages/3f/f3/dbf2a5e11736ea81a66e37288bf9f881143a7822b288a992579ba1b4204d/frozenlist-1.6.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:03572933a1969a6d6ab509d509e5af82ef80d4a5d4e1e9f2e1cdd22c77a3f4d2", size = 395412 }, + { url = "https://files.pythonhosted.org/packages/b3/f1/c63166806b331f05104d8ea385c4acd511598568b1f3e4e8297ca54f2676/frozenlist-1.6.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:77effc978947548b676c54bbd6a08992759ea6f410d4987d69feea9cd0919911", size = 394936 }, + { url = "https://files.pythonhosted.org/packages/ef/ea/4f3e69e179a430473eaa1a75ff986526571215fefc6b9281cdc1f09a4eb8/frozenlist-1.6.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a2bda8be77660ad4089caf2223fdbd6db1858462c4b85b67fbfa22102021e497", size = 391459 }, + { url = "https://files.pythonhosted.org/packages/d3/c3/0fc2c97dea550df9afd072a37c1e95421652e3206bbeaa02378b24c2b480/frozenlist-1.6.0-cp313-cp313t-win32.whl", hash = "sha256:a4d96dc5bcdbd834ec6b0f91027817214216b5b30316494d2b1aebffb87c534f", size = 128797 }, + { url = "https://files.pythonhosted.org/packages/ae/f5/79c9320c5656b1965634fe4be9c82b12a3305bdbc58ad9cb941131107b20/frozenlist-1.6.0-cp313-cp313t-win_amd64.whl", hash = "sha256:e18036cb4caa17ea151fd5f3d70be9d354c99eb8cf817a3ccde8a7873b074348", size = 134709 }, + { url = "https://files.pythonhosted.org/packages/71/3e/b04a0adda73bd52b390d730071c0d577073d3d26740ee1bad25c3ad0f37b/frozenlist-1.6.0-py3-none-any.whl", hash = "sha256:535eec9987adb04701266b92745d6cdcef2e77669299359c3009c3404dd5d191", size = 12404 }, +] + +[[package]] +name = "fsspec" +version = "2025.5.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/00/f7/27f15d41f0ed38e8fcc488584b57e902b331da7f7c6dcda53721b15838fc/fsspec-2025.5.1.tar.gz", hash = "sha256:2e55e47a540b91843b755e83ded97c6e897fa0942b11490113f09e9c443c2475", size = 303033 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bb/61/78c7b3851add1481b048b5fdc29067397a1784e2910592bc81bb3f608635/fsspec-2025.5.1-py3-none-any.whl", hash = "sha256:24d3a2e663d5fc735ab256263c4075f374a174c3410c0b25e5bd1970bceaa462", size = 199052 }, +] + +[package.optional-dependencies] +http = [ + { name = "aiohttp" }, +] + +[[package]] +name = "ftfy" +version = "6.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "wcwidth" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a5/d3/8650919bc3c7c6e90ee3fa7fd618bf373cbbe55dff043bd67353dbb20cd8/ftfy-6.3.1.tar.gz", hash = "sha256:9b3c3d90f84fb267fe64d375a07b7f8912d817cf86009ae134aa03e1819506ec", size = 308927 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ab/6e/81d47999aebc1b155f81eca4477a616a70f238a2549848c38983f3c22a82/ftfy-6.3.1-py3-none-any.whl", hash = "sha256:7c70eb532015cd2f9adb53f101fb6c7945988d023a085d127d1573dc49dd0083", size = 44821 }, +] + +[[package]] +name = "fvcore" +version = "0.1.5.post20221221" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "iopath" }, + { name = "numpy" }, + { name = "pillow" }, + { name = "pyyaml" }, + { name = "tabulate" }, + { name = "termcolor" }, + { name = "tqdm" }, + { name = "yacs" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a5/93/d056a9c4efc6c79ba7b5159cc66bb436db93d2cc46dca18ed65c59cc8e4e/fvcore-0.1.5.post20221221.tar.gz", hash = "sha256:f2fb0bb90572ae651c11c78e20493ed19b2240550a7e4bbb2d6de87bdd037860", size = 50217 } + +[[package]] +name = "gdown" +version = "5.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "beautifulsoup4" }, + { name = "filelock" }, + { name = "requests", extra = ["socks"] }, + { name = "tqdm" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/09/6a/37e6b70c5bda3161e40265861e63b64a86bfc6ca6a8f1c35328a675c84fd/gdown-5.2.0.tar.gz", hash = "sha256:2145165062d85520a3cd98b356c9ed522c5e7984d408535409fd46f94defc787", size = 284647 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/70/e07c381e6488a77094f04c85c9caf1c8008cdc30778f7019bc52e5285ef0/gdown-5.2.0-py3-none-any.whl", hash = "sha256:33083832d82b1101bdd0e9df3edd0fbc0e1c5f14c9d8c38d2a35bf1683b526d6", size = 18235 }, +] + +[[package]] +name = "geopandas" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, + { name = "packaging" }, + { name = "pandas" }, + { name = "pyogrio" }, + { name = "pyproj" }, + { name = "shapely" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/98/ca/e62641e5391285cda854c2802e706e6686f62fc9d919ecf78ff7f8d42654/geopandas-1.1.0.tar.gz", hash = "sha256:d176b084170539044ce7554a1219a4433fa1bfba94035b5a519c8986330e429e", size = 331955 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/be/82/79e02a0e5dd4aca81894842b9d6522624a40048a913c6384efb2987a4144/geopandas-1.1.0-py3-none-any.whl", hash = "sha256:b19b18bdc736ee05b237f5e9184211c452768a4c883f7d7f8421b0cbe1da5875", size = 338014 }, +] + +[[package]] +name = "gigapath" +version = "0.1.0" +source = { git = "https://github.com/EzicStar/prov-gigapath.git?rev=d4cf55321df37aaf867e24a31c61bcf490a296eb#d4cf55321df37aaf867e24a31c61bcf490a296eb" } +dependencies = [ + { name = "fairscale" }, + { name = "flash-attn" }, + { name = "fvcore" }, + { name = "iopath" }, + { name = "lifelines" }, + { name = "monai" }, + { name = "ninja" }, + { name = "scikit-image" }, + { name = "scikit-survival" }, + { name = "wandb" }, + { name = "webdataset" }, +] + +[[package]] +name = "gitdb" +version = "4.0.12" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "smmap" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/72/94/63b0fc47eb32792c7ba1fe1b694daec9a63620db1e313033d18140c2320a/gitdb-4.0.12.tar.gz", hash = "sha256:5ef71f855d191a3326fcfbc0d5da835f26b13fbcba60c32c21091c349ffdb571", size = 394684 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl", hash = "sha256:67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf", size = 62794 }, +] + +[[package]] +name = "gitpython" +version = "3.1.44" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "gitdb" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c0/89/37df0b71473153574a5cdef8f242de422a0f5d26d7a9e231e6f169b4ad14/gitpython-3.1.44.tar.gz", hash = "sha256:c87e30b26253bf5418b01b0660f818967f3c503193838337fe5e573331249269", size = 214196 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1d/9a/4114a9057db2f1462d5c8f8390ab7383925fe1ac012eaa42402ad65c2963/GitPython-3.1.44-py3-none-any.whl", hash = "sha256:9e0e10cda9bed1ee64bc9a6de50e7e38a9c9943241cd7f585f6df3ed28011110", size = 207599 }, +] + +[[package]] +name = "h5py" +version = "3.13.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/03/2e/a22d6a8bfa6f8be33e7febd985680fba531562795f0a9077ed1eb047bfb0/h5py-3.13.0.tar.gz", hash = "sha256:1870e46518720023da85d0895a1960ff2ce398c5671eac3b1a41ec696b7105c3", size = 414876 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/86/2b/50b15fdefb577d073b49699e6ea6a0a77a3a1016c2b67e2149fc50124a10/h5py-3.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8a8e38ef4ceb969f832cc230c0cf808c613cc47e31e768fd7b1106c55afa1cb8", size = 3422922 }, + { url = "https://files.pythonhosted.org/packages/94/59/36d87a559cab9c59b59088d52e86008d27a9602ce3afc9d3b51823014bf3/h5py-3.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f35640e81b03c02a88b8bf99fb6a9d3023cc52f7c627694db2f379e0028f2868", size = 2921619 }, + { url = "https://files.pythonhosted.org/packages/37/ef/6f80b19682c0b0835bbee7b253bec9c16af9004f2fd6427b1dd858100273/h5py-3.13.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:337af114616f3656da0c83b68fcf53ecd9ce9989a700b0883a6e7c483c3235d4", size = 4259366 }, + { url = "https://files.pythonhosted.org/packages/03/71/c99f662d4832c8835453cf3476f95daa28372023bda4aa1fca9e97c24f09/h5py-3.13.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:782ff0ac39f455f21fd1c8ebc007328f65f43d56718a89327eec76677ebf238a", size = 4509058 }, + { url = "https://files.pythonhosted.org/packages/56/89/e3ff23e07131ff73a72a349be9639e4de84e163af89c1c218b939459a98a/h5py-3.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:22ffe2a25770a2d67213a1b94f58006c14dce06933a42d2aaa0318c5868d1508", size = 2966428 }, + { url = "https://files.pythonhosted.org/packages/d8/20/438f6366ba4ded80eadb38f8927f5e2cd6d2e087179552f20ae3dbcd5d5b/h5py-3.13.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:477c58307b6b9a2509c59c57811afb9f598aedede24a67da808262dfa0ee37b4", size = 3384442 }, + { url = "https://files.pythonhosted.org/packages/10/13/cc1cb7231399617d9951233eb12fddd396ff5d4f7f057ee5d2b1ca0ee7e7/h5py-3.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:57c4c74f627c616f02b7aec608a8c706fe08cb5b0ba7c08555a4eb1dde20805a", size = 2917567 }, + { url = "https://files.pythonhosted.org/packages/9e/d9/aed99e1c858dc698489f916eeb7c07513bc864885d28ab3689d572ba0ea0/h5py-3.13.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:357e6dc20b101a805ccfd0024731fbaf6e8718c18c09baf3b5e4e9d198d13fca", size = 4669544 }, + { url = "https://files.pythonhosted.org/packages/a7/da/3c137006ff5f0433f0fb076b1ebe4a7bf7b5ee1e8811b5486af98b500dd5/h5py-3.13.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d6f13f9b5ce549448c01e4dfe08ea8d1772e6078799af2c1c8d09e941230a90d", size = 4932139 }, + { url = "https://files.pythonhosted.org/packages/25/61/d897952629cae131c19d4c41b2521e7dd6382f2d7177c87615c2e6dced1a/h5py-3.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:21daf38171753899b5905f3d82c99b0b1ec2cbbe282a037cad431feb620e62ec", size = 2954179 }, + { url = "https://files.pythonhosted.org/packages/60/43/f276f27921919a9144074320ce4ca40882fc67b3cfee81c3f5c7df083e97/h5py-3.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e520ec76de00943dd017c8ea3f354fa1d2f542eac994811943a8faedf2a7d5cb", size = 3358040 }, + { url = "https://files.pythonhosted.org/packages/1b/86/ad4a4cf781b08d4572be8bbdd8f108bb97b266a14835c640dc43dafc0729/h5py-3.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e79d8368cd9295045956bfb436656bea3f915beaa11d342e9f79f129f5178763", size = 2892766 }, + { url = "https://files.pythonhosted.org/packages/69/84/4c6367d6b58deaf0fa84999ec819e7578eee96cea6cbd613640d0625ed5e/h5py-3.13.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:56dd172d862e850823c4af02dc4ddbc308f042b85472ffdaca67f1598dff4a57", size = 4664255 }, + { url = "https://files.pythonhosted.org/packages/fd/41/bc2df86b72965775f6d621e0ee269a5f3ac23e8f870abf519de9c7d93b4d/h5py-3.13.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be949b46b7388074c5acae017fbbe3e5ba303fd9daaa52157fdfef30bbdacadd", size = 4927580 }, + { url = "https://files.pythonhosted.org/packages/97/34/165b87ea55184770a0c1fcdb7e017199974ad2e271451fd045cfe35f3add/h5py-3.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:4f97ecde7ac6513b21cd95efdfc38dc6d19f96f6ca6f2a30550e94e551458e0a", size = 2940890 }, +] + +[[package]] +name = "hf-xet" +version = "1.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/95/be/58f20728a5b445f8b064e74f0618897b3439f5ef90934da1916b9dfac76f/hf_xet-1.1.2.tar.gz", hash = "sha256:3712d6d4819d3976a1c18e36db9f503e296283f9363af818f50703506ed63da3", size = 467009 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/45/ae/f1a63f75d9886f18a80220ba31a1c7b9c4752f03aae452f358f538c6a991/hf_xet-1.1.2-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:dfd1873fd648488c70735cb60f7728512bca0e459e61fcd107069143cd798469", size = 2642559 }, + { url = "https://files.pythonhosted.org/packages/50/ab/d2c83ae18f1015d926defd5bfbe94c62d15e93f900e6a192e318ee947105/hf_xet-1.1.2-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:29b584983b2d977c44157d9241dcf0fd50acde0b7bff8897fe4386912330090d", size = 2541360 }, + { url = "https://files.pythonhosted.org/packages/9f/a7/693dc9f34f979e30a378125e2150a0b2d8d166e6d83ce3950eeb81e560aa/hf_xet-1.1.2-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b29ac84298147fe9164cc55ad994ba47399f90b5d045b0b803b99cf5f06d8ec", size = 5183081 }, + { url = "https://files.pythonhosted.org/packages/3d/23/c48607883f692a36c0a7735f47f98bad32dbe459a32d1568c0f21576985d/hf_xet-1.1.2-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:d921ba32615676e436a0d15e162331abc9ed43d440916b1d836dc27ce1546173", size = 5356100 }, + { url = "https://files.pythonhosted.org/packages/eb/5b/b2316c7f1076da0582b52ea228f68bea95e243c388440d1dc80297c9d813/hf_xet-1.1.2-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:d9b03c34e13c44893ab6e8fea18ee8d2a6878c15328dd3aabedbdd83ee9f2ed3", size = 5647688 }, + { url = "https://files.pythonhosted.org/packages/2c/98/e6995f0fa579929da7795c961f403f4ee84af36c625963f52741d56f242c/hf_xet-1.1.2-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:01b18608955b3d826307d37da8bd38b28a46cd2d9908b3a3655d1363274f941a", size = 5322627 }, + { url = "https://files.pythonhosted.org/packages/59/40/8f1d5a44a64d8bf9e3c19576e789f716af54875b46daae65426714e75db1/hf_xet-1.1.2-cp37-abi3-win_amd64.whl", hash = "sha256:3562902c81299b09f3582ddfb324400c6a901a2f3bc854f83556495755f4954c", size = 2739542 }, +] + +[[package]] +name = "huggingface-hub" +version = "0.32.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "filelock" }, + { name = "fsspec" }, + { name = "hf-xet", marker = "platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'arm64' or platform_machine == 'x86_64'" }, + { name = "packaging" }, + { name = "pyyaml" }, + { name = "requests" }, + { name = "tqdm" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/59/74/c4961b31e0f142a032ea24f477c3a7524dfabfd8126398a968b3cc6bf804/huggingface_hub-0.32.3.tar.gz", hash = "sha256:752c889ebf3a63cbd39803f6d87ccc135a463bbcb36abfa2faff0ccbf1cec087", size = 424525 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/dc/4f4d8080cbce7a38c1d0f1ba4932f9134480b9761af8ef4c65d49254b2bd/huggingface_hub-0.32.3-py3-none-any.whl", hash = "sha256:e46f7ea7fe2b5e5f67cc4e37eb201140091946a314d7c2b134a9673dadd80b6a", size = 512094 }, +] + +[[package]] +name = "idna" +version = "3.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 }, +] + +[[package]] +name = "imageio" +version = "2.37.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, + { name = "pillow" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0c/47/57e897fb7094afb2d26e8b2e4af9a45c7cf1a405acdeeca001fdf2c98501/imageio-2.37.0.tar.gz", hash = "sha256:71b57b3669666272c818497aebba2b4c5f20d5b37c81720e5e1a56d59c492996", size = 389963 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/bd/b394387b598ed84d8d0fa90611a90bee0adc2021820ad5729f7ced74a8e2/imageio-2.37.0-py3-none-any.whl", hash = "sha256:11efa15b87bc7871b61590326b2d635439acc321cf7f8ce996f812543ce10eed", size = 315796 }, +] + +[[package]] +name = "iniconfig" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050 }, +] + +[[package]] +name = "interface-meta" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4d/75/10526292b332f3479c246750a96f6ec11a28e297839a9c25583b2aadc119/interface_meta-1.3.0.tar.gz", hash = "sha256:8a4493f8bdb73fb9655dcd5115bc897e207319e36c8835f39c516a2d7e9d79a1", size = 15007 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/02/3f/a6ec28c88e2d8e54d32598a1e0b5208a4baa72a8e7f6e241beab5731eb9d/interface_meta-1.3.0-py3-none-any.whl", hash = "sha256:de35dc5241431886e709e20a14d6597ed07c9f1e8b4bfcffde2190ca5b700ee8", size = 14854 }, +] + +[[package]] +name = "iopath" +version = "0.1.10" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "portalocker" }, + { name = "tqdm" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/72/73/b3d451dfc523756cf177d3ebb0af76dc7751b341c60e2a21871be400ae29/iopath-0.1.10.tar.gz", hash = "sha256:3311c16a4d9137223e20f141655759933e1eda24f8bff166af834af3c645ef01", size = 42226 } + +[[package]] +name = "ipykernel" +version = "6.29.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "appnope", marker = "sys_platform == 'darwin'" }, + { name = "comm" }, + { name = "debugpy" }, + { name = "ipython" }, + { name = "jupyter-client" }, + { name = "jupyter-core" }, + { name = "matplotlib-inline" }, + { name = "nest-asyncio" }, + { name = "packaging" }, + { name = "psutil" }, + { name = "pyzmq" }, + { name = "tornado" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e9/5c/67594cb0c7055dc50814b21731c22a601101ea3b1b50a9a1b090e11f5d0f/ipykernel-6.29.5.tar.gz", hash = "sha256:f093a22c4a40f8828f8e330a9c297cb93dcab13bd9678ded6de8e5cf81c56215", size = 163367 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/94/5c/368ae6c01c7628438358e6d337c19b05425727fbb221d2a3c4303c372f42/ipykernel-6.29.5-py3-none-any.whl", hash = "sha256:afdb66ba5aa354b09b91379bac28ae4afebbb30e8b39510c9690afb7a10421b5", size = 117173 }, +] + +[[package]] +name = "ipython" +version = "9.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "decorator" }, + { name = "ipython-pygments-lexers" }, + { name = "jedi" }, + { name = "matplotlib-inline" }, + { name = "pexpect", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, + { name = "prompt-toolkit" }, + { name = "pygments" }, + { name = "stack-data" }, + { name = "traitlets" }, + { name = "typing-extensions", marker = "python_full_version < '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/dc/09/4c7e06b96fbd203e06567b60fb41b06db606b6a82db6db7b2c85bb72a15c/ipython-9.3.0.tar.gz", hash = "sha256:79eb896f9f23f50ad16c3bc205f686f6e030ad246cc309c6279a242b14afe9d8", size = 4426460 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3c/99/9ed3d52d00f1846679e3aa12e2326ac7044b5e7f90dc822b60115fa533ca/ipython-9.3.0-py3-none-any.whl", hash = "sha256:1a0b6dd9221a1f5dddf725b57ac0cb6fddc7b5f470576231ae9162b9b3455a04", size = 605320 }, +] + +[[package]] +name = "ipython-pygments-lexers" +version = "1.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ef/4c/5dd1d8af08107f88c7f741ead7a40854b8ac24ddf9ae850afbcf698aa552/ipython_pygments_lexers-1.1.1.tar.gz", hash = "sha256:09c0138009e56b6854f9535736f4171d855c8c08a563a0dcd8022f78355c7e81", size = 8393 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d9/33/1f075bf72b0b747cb3288d011319aaf64083cf2efef8354174e3ed4540e2/ipython_pygments_lexers-1.1.1-py3-none-any.whl", hash = "sha256:a9462224a505ade19a605f71f8fa63c2048833ce50abc86768a0d81d876dc81c", size = 8074 }, +] + +[[package]] +name = "jaxtyping" +version = "0.3.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "wadler-lindig" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/88/a8/416bd7ea110ec6b68e8868b0f99c985c735adcf7badc491d3c343937260a/jaxtyping-0.3.2.tar.gz", hash = "sha256:f30483fac4b42e915db8ad2414a85c3b63284aa7d3c100b96b59f755cf4a86ad", size = 44989 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c9/b9/281e10e2d967ea5e481683eaec99f55ac5a61085ee60551c36942ef32bef/jaxtyping-0.3.2-py3-none-any.whl", hash = "sha256:6a020fd276226ddb5ac4f5725323843dd65e3c7e85c64fd62431e5f738c74e04", size = 55409 }, +] + +[[package]] +name = "jedi" +version = "0.19.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "parso" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/72/3a/79a912fbd4d8dd6fbb02bf69afd3bb72cf0c729bb3063c6f4498603db17a/jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0", size = 1231287 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c0/5a/9cac0c82afec3d09ccd97c8b6502d48f165f9124db81b4bcb90b4af974ee/jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9", size = 1572278 }, +] + +[[package]] +name = "jinja2" +version = "3.1.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899 }, +] + +[[package]] +name = "joblib" +version = "1.5.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/dc/fe/0f5a938c54105553436dbff7a61dc4fed4b1b2c98852f8833beaf4d5968f/joblib-1.5.1.tar.gz", hash = "sha256:f4f86e351f39fe3d0d32a9f2c3d8af1ee4cec285aafcb27003dda5205576b444", size = 330475 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7d/4f/1195bbac8e0c2acc5f740661631d8d750dc38d4a32b23ee5df3cde6f4e0d/joblib-1.5.1-py3-none-any.whl", hash = "sha256:4719a31f054c7d766948dcd83e9613686b27114f190f717cec7eaa2084f8a74a", size = 307746 }, +] + +[[package]] +name = "jupyter-client" +version = "8.6.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jupyter-core" }, + { name = "python-dateutil" }, + { name = "pyzmq" }, + { name = "tornado" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/71/22/bf9f12fdaeae18019a468b68952a60fe6dbab5d67cd2a103cac7659b41ca/jupyter_client-8.6.3.tar.gz", hash = "sha256:35b3a0947c4a6e9d589eb97d7d4cd5e90f910ee73101611f01283732bd6d9419", size = 342019 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/11/85/b0394e0b6fcccd2c1eeefc230978a6f8cb0c5df1e4cd3e7625735a0d7d1e/jupyter_client-8.6.3-py3-none-any.whl", hash = "sha256:e8a19cc986cc45905ac3362915f410f3af85424b4c0905e94fa5f2cb08e8f23f", size = 106105 }, +] + +[[package]] +name = "jupyter-core" +version = "5.8.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "platformdirs" }, + { name = "pywin32", marker = "platform_python_implementation != 'PyPy' and sys_platform == 'win32'" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/99/1b/72906d554acfeb588332eaaa6f61577705e9ec752ddb486f302dafa292d9/jupyter_core-5.8.1.tar.gz", hash = "sha256:0a5f9706f70e64786b75acba995988915ebd4601c8a52e534a40b51c95f59941", size = 88923 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2f/57/6bffd4b20b88da3800c5d691e0337761576ee688eb01299eae865689d2df/jupyter_core-5.8.1-py3-none-any.whl", hash = "sha256:c28d268fc90fb53f1338ded2eb410704c5449a358406e8a948b75706e24863d0", size = 28880 }, +] + +[[package]] +name = "kiwisolver" +version = "1.4.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/82/59/7c91426a8ac292e1cdd53a63b6d9439abd573c875c3f92c146767dd33faf/kiwisolver-1.4.8.tar.gz", hash = "sha256:23d5f023bdc8c7e54eb65f03ca5d5bb25b601eac4d7f1a042888a1f45237987e", size = 97538 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/da/ed/c913ee28936c371418cb167b128066ffb20bbf37771eecc2c97edf8a6e4c/kiwisolver-1.4.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a4d3601908c560bdf880f07d94f31d734afd1bb71e96585cace0e38ef44c6d84", size = 124635 }, + { url = "https://files.pythonhosted.org/packages/4c/45/4a7f896f7467aaf5f56ef093d1f329346f3b594e77c6a3c327b2d415f521/kiwisolver-1.4.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:856b269c4d28a5c0d5e6c1955ec36ebfd1651ac00e1ce0afa3e28da95293b561", size = 66717 }, + { url = "https://files.pythonhosted.org/packages/5f/b4/c12b3ac0852a3a68f94598d4c8d569f55361beef6159dce4e7b624160da2/kiwisolver-1.4.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c2b9a96e0f326205af81a15718a9073328df1173a2619a68553decb7097fd5d7", size = 65413 }, + { url = "https://files.pythonhosted.org/packages/a9/98/1df4089b1ed23d83d410adfdc5947245c753bddfbe06541c4aae330e9e70/kiwisolver-1.4.8-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c5020c83e8553f770cb3b5fc13faac40f17e0b205bd237aebd21d53d733adb03", size = 1343994 }, + { url = "https://files.pythonhosted.org/packages/8d/bf/b4b169b050c8421a7c53ea1ea74e4ef9c335ee9013216c558a047f162d20/kiwisolver-1.4.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dace81d28c787956bfbfbbfd72fdcef014f37d9b48830829e488fdb32b49d954", size = 1434804 }, + { url = "https://files.pythonhosted.org/packages/66/5a/e13bd341fbcf73325ea60fdc8af752addf75c5079867af2e04cc41f34434/kiwisolver-1.4.8-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:11e1022b524bd48ae56c9b4f9296bce77e15a2e42a502cceba602f804b32bb79", size = 1450690 }, + { url = "https://files.pythonhosted.org/packages/9b/4f/5955dcb376ba4a830384cc6fab7d7547bd6759fe75a09564910e9e3bb8ea/kiwisolver-1.4.8-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b9b4d2892fefc886f30301cdd80debd8bb01ecdf165a449eb6e78f79f0fabd6", size = 1376839 }, + { url = "https://files.pythonhosted.org/packages/3a/97/5edbed69a9d0caa2e4aa616ae7df8127e10f6586940aa683a496c2c280b9/kiwisolver-1.4.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a96c0e790ee875d65e340ab383700e2b4891677b7fcd30a699146f9384a2bb0", size = 1435109 }, + { url = "https://files.pythonhosted.org/packages/13/fc/e756382cb64e556af6c1809a1bbb22c141bbc2445049f2da06b420fe52bf/kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:23454ff084b07ac54ca8be535f4174170c1094a4cff78fbae4f73a4bcc0d4dab", size = 2245269 }, + { url = "https://files.pythonhosted.org/packages/76/15/e59e45829d7f41c776d138245cabae6515cb4eb44b418f6d4109c478b481/kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:87b287251ad6488e95b4f0b4a79a6d04d3ea35fde6340eb38fbd1ca9cd35bbbc", size = 2393468 }, + { url = "https://files.pythonhosted.org/packages/e9/39/483558c2a913ab8384d6e4b66a932406f87c95a6080112433da5ed668559/kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:b21dbe165081142b1232a240fc6383fd32cdd877ca6cc89eab93e5f5883e1c25", size = 2355394 }, + { url = "https://files.pythonhosted.org/packages/01/aa/efad1fbca6570a161d29224f14b082960c7e08268a133fe5dc0f6906820e/kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:768cade2c2df13db52475bd28d3a3fac8c9eff04b0e9e2fda0f3760f20b3f7fc", size = 2490901 }, + { url = "https://files.pythonhosted.org/packages/c9/4f/15988966ba46bcd5ab9d0c8296914436720dd67fca689ae1a75b4ec1c72f/kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d47cfb2650f0e103d4bf68b0b5804c68da97272c84bb12850d877a95c056bd67", size = 2312306 }, + { url = "https://files.pythonhosted.org/packages/2d/27/bdf1c769c83f74d98cbc34483a972f221440703054894a37d174fba8aa68/kiwisolver-1.4.8-cp311-cp311-win_amd64.whl", hash = "sha256:ed33ca2002a779a2e20eeb06aea7721b6e47f2d4b8a8ece979d8ba9e2a167e34", size = 71966 }, + { url = "https://files.pythonhosted.org/packages/4a/c9/9642ea855604aeb2968a8e145fc662edf61db7632ad2e4fb92424be6b6c0/kiwisolver-1.4.8-cp311-cp311-win_arm64.whl", hash = "sha256:16523b40aab60426ffdebe33ac374457cf62863e330a90a0383639ce14bf44b2", size = 65311 }, + { url = "https://files.pythonhosted.org/packages/fc/aa/cea685c4ab647f349c3bc92d2daf7ae34c8e8cf405a6dcd3a497f58a2ac3/kiwisolver-1.4.8-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d6af5e8815fd02997cb6ad9bbed0ee1e60014438ee1a5c2444c96f87b8843502", size = 124152 }, + { url = "https://files.pythonhosted.org/packages/c5/0b/8db6d2e2452d60d5ebc4ce4b204feeb16176a851fd42462f66ade6808084/kiwisolver-1.4.8-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:bade438f86e21d91e0cf5dd7c0ed00cda0f77c8c1616bd83f9fc157fa6760d31", size = 66555 }, + { url = "https://files.pythonhosted.org/packages/60/26/d6a0db6785dd35d3ba5bf2b2df0aedc5af089962c6eb2cbf67a15b81369e/kiwisolver-1.4.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b83dc6769ddbc57613280118fb4ce3cd08899cc3369f7d0e0fab518a7cf37fdb", size = 65067 }, + { url = "https://files.pythonhosted.org/packages/c9/ed/1d97f7e3561e09757a196231edccc1bcf59d55ddccefa2afc9c615abd8e0/kiwisolver-1.4.8-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:111793b232842991be367ed828076b03d96202c19221b5ebab421ce8bcad016f", size = 1378443 }, + { url = "https://files.pythonhosted.org/packages/29/61/39d30b99954e6b46f760e6289c12fede2ab96a254c443639052d1b573fbc/kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:257af1622860e51b1a9d0ce387bf5c2c4f36a90594cb9514f55b074bcc787cfc", size = 1472728 }, + { url = "https://files.pythonhosted.org/packages/0c/3e/804163b932f7603ef256e4a715e5843a9600802bb23a68b4e08c8c0ff61d/kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:69b5637c3f316cab1ec1c9a12b8c5f4750a4c4b71af9157645bf32830e39c03a", size = 1478388 }, + { url = "https://files.pythonhosted.org/packages/8a/9e/60eaa75169a154700be74f875a4d9961b11ba048bef315fbe89cb6999056/kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:782bb86f245ec18009890e7cb8d13a5ef54dcf2ebe18ed65f795e635a96a1c6a", size = 1413849 }, + { url = "https://files.pythonhosted.org/packages/bc/b3/9458adb9472e61a998c8c4d95cfdfec91c73c53a375b30b1428310f923e4/kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc978a80a0db3a66d25767b03688f1147a69e6237175c0f4ffffaaedf744055a", size = 1475533 }, + { url = "https://files.pythonhosted.org/packages/e4/7a/0a42d9571e35798de80aef4bb43a9b672aa7f8e58643d7bd1950398ffb0a/kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:36dbbfd34838500a31f52c9786990d00150860e46cd5041386f217101350f0d3", size = 2268898 }, + { url = "https://files.pythonhosted.org/packages/d9/07/1255dc8d80271400126ed8db35a1795b1a2c098ac3a72645075d06fe5c5d/kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:eaa973f1e05131de5ff3569bbba7f5fd07ea0595d3870ed4a526d486fe57fa1b", size = 2425605 }, + { url = "https://files.pythonhosted.org/packages/84/df/5a3b4cf13780ef6f6942df67b138b03b7e79e9f1f08f57c49957d5867f6e/kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a66f60f8d0c87ab7f59b6fb80e642ebb29fec354a4dfad687ca4092ae69d04f4", size = 2375801 }, + { url = "https://files.pythonhosted.org/packages/8f/10/2348d068e8b0f635c8c86892788dac7a6b5c0cb12356620ab575775aad89/kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:858416b7fb777a53f0c59ca08190ce24e9abbd3cffa18886a5781b8e3e26f65d", size = 2520077 }, + { url = "https://files.pythonhosted.org/packages/32/d8/014b89fee5d4dce157d814303b0fce4d31385a2af4c41fed194b173b81ac/kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:085940635c62697391baafaaeabdf3dd7a6c3643577dde337f4d66eba021b2b8", size = 2338410 }, + { url = "https://files.pythonhosted.org/packages/bd/72/dfff0cc97f2a0776e1c9eb5bef1ddfd45f46246c6533b0191887a427bca5/kiwisolver-1.4.8-cp312-cp312-win_amd64.whl", hash = "sha256:01c3d31902c7db5fb6182832713d3b4122ad9317c2c5877d0539227d96bb2e50", size = 71853 }, + { url = "https://files.pythonhosted.org/packages/dc/85/220d13d914485c0948a00f0b9eb419efaf6da81b7d72e88ce2391f7aed8d/kiwisolver-1.4.8-cp312-cp312-win_arm64.whl", hash = "sha256:a3c44cb68861de93f0c4a8175fbaa691f0aa22550c331fefef02b618a9dcb476", size = 65424 }, + { url = "https://files.pythonhosted.org/packages/79/b3/e62464a652f4f8cd9006e13d07abad844a47df1e6537f73ddfbf1bc997ec/kiwisolver-1.4.8-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:1c8ceb754339793c24aee1c9fb2485b5b1f5bb1c2c214ff13368431e51fc9a09", size = 124156 }, + { url = "https://files.pythonhosted.org/packages/8d/2d/f13d06998b546a2ad4f48607a146e045bbe48030774de29f90bdc573df15/kiwisolver-1.4.8-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:54a62808ac74b5e55a04a408cda6156f986cefbcf0ada13572696b507cc92fa1", size = 66555 }, + { url = "https://files.pythonhosted.org/packages/59/e3/b8bd14b0a54998a9fd1e8da591c60998dc003618cb19a3f94cb233ec1511/kiwisolver-1.4.8-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:68269e60ee4929893aad82666821aaacbd455284124817af45c11e50a4b42e3c", size = 65071 }, + { url = "https://files.pythonhosted.org/packages/f0/1c/6c86f6d85ffe4d0ce04228d976f00674f1df5dc893bf2dd4f1928748f187/kiwisolver-1.4.8-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:34d142fba9c464bc3bbfeff15c96eab0e7310343d6aefb62a79d51421fcc5f1b", size = 1378053 }, + { url = "https://files.pythonhosted.org/packages/4e/b9/1c6e9f6dcb103ac5cf87cb695845f5fa71379021500153566d8a8a9fc291/kiwisolver-1.4.8-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ddc373e0eef45b59197de815b1b28ef89ae3955e7722cc9710fb91cd77b7f47", size = 1472278 }, + { url = "https://files.pythonhosted.org/packages/ee/81/aca1eb176de671f8bda479b11acdc42c132b61a2ac861c883907dde6debb/kiwisolver-1.4.8-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:77e6f57a20b9bd4e1e2cedda4d0b986ebd0216236f0106e55c28aea3d3d69b16", size = 1478139 }, + { url = "https://files.pythonhosted.org/packages/49/f4/e081522473671c97b2687d380e9e4c26f748a86363ce5af48b4a28e48d06/kiwisolver-1.4.8-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:08e77738ed7538f036cd1170cbed942ef749137b1311fa2bbe2a7fda2f6bf3cc", size = 1413517 }, + { url = "https://files.pythonhosted.org/packages/8f/e9/6a7d025d8da8c4931522922cd706105aa32b3291d1add8c5427cdcd66e63/kiwisolver-1.4.8-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a5ce1e481a74b44dd5e92ff03ea0cb371ae7a0268318e202be06c8f04f4f1246", size = 1474952 }, + { url = "https://files.pythonhosted.org/packages/82/13/13fa685ae167bee5d94b415991c4fc7bb0a1b6ebea6e753a87044b209678/kiwisolver-1.4.8-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:fc2ace710ba7c1dfd1a3b42530b62b9ceed115f19a1656adefce7b1782a37794", size = 2269132 }, + { url = "https://files.pythonhosted.org/packages/ef/92/bb7c9395489b99a6cb41d502d3686bac692586db2045adc19e45ee64ed23/kiwisolver-1.4.8-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:3452046c37c7692bd52b0e752b87954ef86ee2224e624ef7ce6cb21e8c41cc1b", size = 2425997 }, + { url = "https://files.pythonhosted.org/packages/ed/12/87f0e9271e2b63d35d0d8524954145837dd1a6c15b62a2d8c1ebe0f182b4/kiwisolver-1.4.8-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:7e9a60b50fe8b2ec6f448fe8d81b07e40141bfced7f896309df271a0b92f80f3", size = 2376060 }, + { url = "https://files.pythonhosted.org/packages/02/6e/c8af39288edbce8bf0fa35dee427b082758a4b71e9c91ef18fa667782138/kiwisolver-1.4.8-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:918139571133f366e8362fa4a297aeba86c7816b7ecf0bc79168080e2bd79957", size = 2520471 }, + { url = "https://files.pythonhosted.org/packages/13/78/df381bc7b26e535c91469f77f16adcd073beb3e2dd25042efd064af82323/kiwisolver-1.4.8-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e063ef9f89885a1d68dd8b2e18f5ead48653176d10a0e324e3b0030e3a69adeb", size = 2338793 }, + { url = "https://files.pythonhosted.org/packages/d0/dc/c1abe38c37c071d0fc71c9a474fd0b9ede05d42f5a458d584619cfd2371a/kiwisolver-1.4.8-cp313-cp313-win_amd64.whl", hash = "sha256:a17b7c4f5b2c51bb68ed379defd608a03954a1845dfed7cc0117f1cc8a9b7fd2", size = 71855 }, + { url = "https://files.pythonhosted.org/packages/a0/b6/21529d595b126ac298fdd90b705d87d4c5693de60023e0efcb4f387ed99e/kiwisolver-1.4.8-cp313-cp313-win_arm64.whl", hash = "sha256:3cd3bc628b25f74aedc6d374d5babf0166a92ff1317f46267f12d2ed54bc1d30", size = 65430 }, + { url = "https://files.pythonhosted.org/packages/34/bd/b89380b7298e3af9b39f49334e3e2a4af0e04819789f04b43d560516c0c8/kiwisolver-1.4.8-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:370fd2df41660ed4e26b8c9d6bbcad668fbe2560462cba151a721d49e5b6628c", size = 126294 }, + { url = "https://files.pythonhosted.org/packages/83/41/5857dc72e5e4148eaac5aa76e0703e594e4465f8ab7ec0fc60e3a9bb8fea/kiwisolver-1.4.8-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:84a2f830d42707de1d191b9490ac186bf7997a9495d4e9072210a1296345f7dc", size = 67736 }, + { url = "https://files.pythonhosted.org/packages/e1/d1/be059b8db56ac270489fb0b3297fd1e53d195ba76e9bbb30e5401fa6b759/kiwisolver-1.4.8-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:7a3ad337add5148cf51ce0b55642dc551c0b9d6248458a757f98796ca7348712", size = 66194 }, + { url = "https://files.pythonhosted.org/packages/e1/83/4b73975f149819eb7dcf9299ed467eba068ecb16439a98990dcb12e63fdd/kiwisolver-1.4.8-cp313-cp313t-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7506488470f41169b86d8c9aeff587293f530a23a23a49d6bc64dab66bedc71e", size = 1465942 }, + { url = "https://files.pythonhosted.org/packages/c7/2c/30a5cdde5102958e602c07466bce058b9d7cb48734aa7a4327261ac8e002/kiwisolver-1.4.8-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f0121b07b356a22fb0414cec4666bbe36fd6d0d759db3d37228f496ed67c880", size = 1595341 }, + { url = "https://files.pythonhosted.org/packages/ff/9b/1e71db1c000385aa069704f5990574b8244cce854ecd83119c19e83c9586/kiwisolver-1.4.8-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d6d6bd87df62c27d4185de7c511c6248040afae67028a8a22012b010bc7ad062", size = 1598455 }, + { url = "https://files.pythonhosted.org/packages/85/92/c8fec52ddf06231b31cbb779af77e99b8253cd96bd135250b9498144c78b/kiwisolver-1.4.8-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:291331973c64bb9cce50bbe871fb2e675c4331dab4f31abe89f175ad7679a4d7", size = 1522138 }, + { url = "https://files.pythonhosted.org/packages/0b/51/9eb7e2cd07a15d8bdd976f6190c0164f92ce1904e5c0c79198c4972926b7/kiwisolver-1.4.8-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:893f5525bb92d3d735878ec00f781b2de998333659507d29ea4466208df37bed", size = 1582857 }, + { url = "https://files.pythonhosted.org/packages/0f/95/c5a00387a5405e68ba32cc64af65ce881a39b98d73cc394b24143bebc5b8/kiwisolver-1.4.8-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b47a465040146981dc9db8647981b8cb96366fbc8d452b031e4f8fdffec3f26d", size = 2293129 }, + { url = "https://files.pythonhosted.org/packages/44/83/eeb7af7d706b8347548313fa3a3a15931f404533cc54fe01f39e830dd231/kiwisolver-1.4.8-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:99cea8b9dd34ff80c521aef46a1dddb0dcc0283cf18bde6d756f1e6f31772165", size = 2421538 }, + { url = "https://files.pythonhosted.org/packages/05/f9/27e94c1b3eb29e6933b6986ffc5fa1177d2cd1f0c8efc5f02c91c9ac61de/kiwisolver-1.4.8-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:151dffc4865e5fe6dafce5480fab84f950d14566c480c08a53c663a0020504b6", size = 2390661 }, + { url = "https://files.pythonhosted.org/packages/d9/d4/3c9735faa36ac591a4afcc2980d2691000506050b7a7e80bcfe44048daa7/kiwisolver-1.4.8-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:577facaa411c10421314598b50413aa1ebcf5126f704f1e5d72d7e4e9f020d90", size = 2546710 }, + { url = "https://files.pythonhosted.org/packages/4c/fa/be89a49c640930180657482a74970cdcf6f7072c8d2471e1babe17a222dc/kiwisolver-1.4.8-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:be4816dc51c8a471749d664161b434912eee82f2ea66bd7628bd14583a833e85", size = 2349213 }, +] + +[[package]] +name = "lazy-loader" +version = "0.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6f/6b/c875b30a1ba490860c93da4cabf479e03f584eba06fe5963f6f6644653d8/lazy_loader-0.4.tar.gz", hash = "sha256:47c75182589b91a4e1a85a136c074285a5ad4d9f39c63e0d7fb76391c4574cd1", size = 15431 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/83/60/d497a310bde3f01cb805196ac61b7ad6dc5dcf8dce66634dc34364b20b4f/lazy_loader-0.4-py3-none-any.whl", hash = "sha256:342aa8e14d543a154047afb4ba8ef17f5563baad3fc610d7b15b213b0f119efc", size = 12097 }, +] + +[[package]] +name = "lifelines" +version = "0.30.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "autograd" }, + { name = "autograd-gamma" }, + { name = "formulaic" }, + { name = "matplotlib" }, + { name = "numpy" }, + { name = "pandas" }, + { name = "scipy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ea/4f/f0b363278d40baf7d7a03217bee839cb880946c62109f243391c8754bb09/lifelines-0.30.0.tar.gz", hash = "sha256:f7f6f6275fcb167fe0f5b1ef98f868993f9c074cb74b1dd6e92736efa854be18", size = 383221 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/14/f7/379e185a75ac8166ac70756d0ba68d9a2b02b555c7fde4983246752396bd/lifelines-0.30.0-py3-none-any.whl", hash = "sha256:ac7c602c8aceced9770d3977817c9d99c250ed8cd86f2567fa0d23e4e8014bf9", size = 349319 }, +] + +[[package]] +name = "lightning" +version = "2.5.1.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "fsspec", extra = ["http"] }, + { name = "lightning-utilities" }, + { name = "packaging" }, + { name = "pytorch-lightning" }, + { name = "pyyaml" }, + { name = "torch" }, + { name = "torchmetrics" }, + { name = "tqdm" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/97/d0/fb3c5077efdd74c28ea3a277fd80bbf03738d866013a8637691138bfebca/lightning-2.5.1.post0.tar.gz", hash = "sha256:fda1ac63c283b3b08a54be8d905dd88469cf09e9845d36dd28b699e78911cbc8", size = 631113 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/1b/67201d693a575e8a086831710f33e697fab66166223f792e459ef2b84934/lightning-2.5.1.post0-py3-none-any.whl", hash = "sha256:a228a52ca52f0c5006ff327c92b8942f09e1aea3f2d9b0d7c8a209edd5b9e71d", size = 819001 }, +] + +[[package]] +name = "lightning-utilities" +version = "0.14.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging" }, + { name = "setuptools" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0f/bb/63a6a8c9e7a96b6ba92647fa5b1595c2dbee29f8178705adb4704d82ecba/lightning_utilities-0.14.3.tar.gz", hash = "sha256:37e2f83f273890052955a44054382c211a303012ee577619efbaa5df9e65e9f5", size = 30346 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1a/c1/31b3184cba7b257a4a3b5ca5b88b9204ccb7aa02fe3c992280899293ed54/lightning_utilities-0.14.3-py3-none-any.whl", hash = "sha256:4ab9066aa36cd7b93a05713808901909e96cc3f187ea6fd3052b2fd91313b468", size = 28894 }, +] + +[[package]] +name = "madeleine" +version = "0.0.1" +source = { git = "https://github.com/mahmoodlab/MADELEINE.git?rev=de7c85acc2bdad352e6df8eee5694f8b6f288012#de7c85acc2bdad352e6df8eee5694f8b6f288012" } +dependencies = [ + { name = "einops" }, + { name = "geopandas" }, + { name = "h5py" }, + { name = "huggingface-hub" }, + { name = "scikit-learn" }, + { name = "shapely" }, + { name = "torch" }, + { name = "tqdm" }, + { name = "wandb" }, +] + +[[package]] +name = "mamba-ssm" +version = "2.2.4" +source = { git = "https://github.com/KatherLab/mamba.git?rev=ffef06879361ea36893697d88f43d4f76bb28877#ffef06879361ea36893697d88f43d4f76bb28877" } +dependencies = [ + { name = "einops" }, + { name = "ninja" }, + { name = "packaging" }, + { name = "setuptools" }, + { name = "torch" }, + { name = "transformers" }, + { name = "triton", version = "3.2.0", source = { registry = "https://pypi.org/simple" }, marker = "platform_machine != 'aarch64' and sys_platform == 'linux'" }, + { name = "triton", version = "3.3.0", source = { registry = "https://pypi.org/simple" }, marker = "platform_machine == 'aarch64' or sys_platform != 'linux'" }, +] + +[[package]] +name = "markupsafe" +version = "3.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6b/28/bbf83e3f76936960b850435576dd5e67034e200469571be53f69174a2dfd/MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d", size = 14353 }, + { url = "https://files.pythonhosted.org/packages/6c/30/316d194b093cde57d448a4c3209f22e3046c5bb2fb0820b118292b334be7/MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93", size = 12392 }, + { url = "https://files.pythonhosted.org/packages/f2/96/9cdafba8445d3a53cae530aaf83c38ec64c4d5427d975c974084af5bc5d2/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832", size = 23984 }, + { url = "https://files.pythonhosted.org/packages/f1/a4/aefb044a2cd8d7334c8a47d3fb2c9f328ac48cb349468cc31c20b539305f/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84", size = 23120 }, + { url = "https://files.pythonhosted.org/packages/8d/21/5e4851379f88f3fad1de30361db501300d4f07bcad047d3cb0449fc51f8c/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca", size = 23032 }, + { url = "https://files.pythonhosted.org/packages/00/7b/e92c64e079b2d0d7ddf69899c98842f3f9a60a1ae72657c89ce2655c999d/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798", size = 24057 }, + { url = "https://files.pythonhosted.org/packages/f9/ac/46f960ca323037caa0a10662ef97d0a4728e890334fc156b9f9e52bcc4ca/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e", size = 23359 }, + { url = "https://files.pythonhosted.org/packages/69/84/83439e16197337b8b14b6a5b9c2105fff81d42c2a7c5b58ac7b62ee2c3b1/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4", size = 23306 }, + { url = "https://files.pythonhosted.org/packages/9a/34/a15aa69f01e2181ed8d2b685c0d2f6655d5cca2c4db0ddea775e631918cd/MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d", size = 15094 }, + { url = "https://files.pythonhosted.org/packages/da/b8/3a3bd761922d416f3dc5d00bfbed11f66b1ab89a0c2b6e887240a30b0f6b/MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b", size = 15521 }, + { url = "https://files.pythonhosted.org/packages/22/09/d1f21434c97fc42f09d290cbb6350d44eb12f09cc62c9476effdb33a18aa/MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf", size = 14274 }, + { url = "https://files.pythonhosted.org/packages/6b/b0/18f76bba336fa5aecf79d45dcd6c806c280ec44538b3c13671d49099fdd0/MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225", size = 12348 }, + { url = "https://files.pythonhosted.org/packages/e0/25/dd5c0f6ac1311e9b40f4af06c78efde0f3b5cbf02502f8ef9501294c425b/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028", size = 24149 }, + { url = "https://files.pythonhosted.org/packages/f3/f0/89e7aadfb3749d0f52234a0c8c7867877876e0a20b60e2188e9850794c17/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", size = 23118 }, + { url = "https://files.pythonhosted.org/packages/d5/da/f2eeb64c723f5e3777bc081da884b414671982008c47dcc1873d81f625b6/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", size = 22993 }, + { url = "https://files.pythonhosted.org/packages/da/0e/1f32af846df486dce7c227fe0f2398dc7e2e51d4a370508281f3c1c5cddc/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", size = 24178 }, + { url = "https://files.pythonhosted.org/packages/c4/f6/bb3ca0532de8086cbff5f06d137064c8410d10779c4c127e0e47d17c0b71/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", size = 23319 }, + { url = "https://files.pythonhosted.org/packages/a2/82/8be4c96ffee03c5b4a034e60a31294daf481e12c7c43ab8e34a1453ee48b/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", size = 23352 }, + { url = "https://files.pythonhosted.org/packages/51/ae/97827349d3fcffee7e184bdf7f41cd6b88d9919c80f0263ba7acd1bbcb18/MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", size = 15097 }, + { url = "https://files.pythonhosted.org/packages/c1/80/a61f99dc3a936413c3ee4e1eecac96c0da5ed07ad56fd975f1a9da5bc630/MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", size = 15601 }, + { url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274 }, + { url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352 }, + { url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122 }, + { url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085 }, + { url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978 }, + { url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208 }, + { url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357 }, + { url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344 }, + { url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101 }, + { url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603 }, + { url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510 }, + { url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486 }, + { url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480 }, + { url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914 }, + { url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796 }, + { url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473 }, + { url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114 }, + { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098 }, + { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208 }, + { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739 }, +] + +[[package]] +name = "marshmallow" +version = "4.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1e/ff/26df5a9f5ac57ccf693a5854916ab47243039d2aa9e0fe5f5a0331e7b74b/marshmallow-4.0.0.tar.gz", hash = "sha256:3b6e80aac299a7935cfb97ed01d1854fb90b5079430969af92118ea1b12a8d55", size = 220507 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d6/26/6cc45d156f44dbe1d5696d9e54042e4dcaf7b946c0b86df6a97d29706f32/marshmallow-4.0.0-py3-none-any.whl", hash = "sha256:e7b0528337e9990fd64950f8a6b3a1baabed09ad17a0dfb844d701151f92d203", size = 48420 }, +] + +[[package]] +name = "matplotlib" +version = "3.10.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "contourpy" }, + { name = "cycler" }, + { name = "fonttools" }, + { name = "kiwisolver" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "pillow" }, + { name = "pyparsing" }, + { name = "python-dateutil" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/26/91/d49359a21893183ed2a5b6c76bec40e0b1dcbf8ca148f864d134897cfc75/matplotlib-3.10.3.tar.gz", hash = "sha256:2f82d2c5bb7ae93aaaa4cd42aca65d76ce6376f83304fa3a630b569aca274df0", size = 34799811 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f5/bd/af9f655456f60fe1d575f54fb14704ee299b16e999704817a7645dfce6b0/matplotlib-3.10.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:0ef061f74cd488586f552d0c336b2f078d43bc00dc473d2c3e7bfee2272f3fa8", size = 8178873 }, + { url = "https://files.pythonhosted.org/packages/c2/86/e1c86690610661cd716eda5f9d0b35eaf606ae6c9b6736687cfc8f2d0cd8/matplotlib-3.10.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d96985d14dc5f4a736bbea4b9de9afaa735f8a0fc2ca75be2fa9e96b2097369d", size = 8052205 }, + { url = "https://files.pythonhosted.org/packages/54/51/a9f8e49af3883dacddb2da1af5fca1f7468677f1188936452dd9aaaeb9ed/matplotlib-3.10.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7c5f0283da91e9522bdba4d6583ed9d5521566f63729ffb68334f86d0bb98049", size = 8465823 }, + { url = "https://files.pythonhosted.org/packages/e7/e3/c82963a3b86d6e6d5874cbeaa390166458a7f1961bab9feb14d3d1a10f02/matplotlib-3.10.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdfa07c0ec58035242bc8b2c8aae37037c9a886370eef6850703d7583e19964b", size = 8606464 }, + { url = "https://files.pythonhosted.org/packages/0e/34/24da1027e7fcdd9e82da3194c470143c551852757a4b473a09a012f5b945/matplotlib-3.10.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c0b9849a17bce080a16ebcb80a7b714b5677d0ec32161a2cc0a8e5a6030ae220", size = 9413103 }, + { url = "https://files.pythonhosted.org/packages/a6/da/948a017c3ea13fd4a97afad5fdebe2f5bbc4d28c0654510ce6fd6b06b7bd/matplotlib-3.10.3-cp311-cp311-win_amd64.whl", hash = "sha256:eef6ed6c03717083bc6d69c2d7ee8624205c29a8e6ea5a31cd3492ecdbaee1e1", size = 8065492 }, + { url = "https://files.pythonhosted.org/packages/eb/43/6b80eb47d1071f234ef0c96ca370c2ca621f91c12045f1401b5c9b28a639/matplotlib-3.10.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0ab1affc11d1f495ab9e6362b8174a25afc19c081ba5b0775ef00533a4236eea", size = 8179689 }, + { url = "https://files.pythonhosted.org/packages/0f/70/d61a591958325c357204870b5e7b164f93f2a8cca1dc6ce940f563909a13/matplotlib-3.10.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2a818d8bdcafa7ed2eed74487fdb071c09c1ae24152d403952adad11fa3c65b4", size = 8050466 }, + { url = "https://files.pythonhosted.org/packages/e7/75/70c9d2306203148cc7902a961240c5927dd8728afedf35e6a77e105a2985/matplotlib-3.10.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:748ebc3470c253e770b17d8b0557f0aa85cf8c63fd52f1a61af5b27ec0b7ffee", size = 8456252 }, + { url = "https://files.pythonhosted.org/packages/c4/91/ba0ae1ff4b3f30972ad01cd4a8029e70a0ec3b8ea5be04764b128b66f763/matplotlib-3.10.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed70453fd99733293ace1aec568255bc51c6361cb0da94fa5ebf0649fdb2150a", size = 8601321 }, + { url = "https://files.pythonhosted.org/packages/d2/88/d636041eb54a84b889e11872d91f7cbf036b3b0e194a70fa064eb8b04f7a/matplotlib-3.10.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dbed9917b44070e55640bd13419de83b4c918e52d97561544814ba463811cbc7", size = 9406972 }, + { url = "https://files.pythonhosted.org/packages/b1/79/0d1c165eac44405a86478082e225fce87874f7198300bbebc55faaf6d28d/matplotlib-3.10.3-cp312-cp312-win_amd64.whl", hash = "sha256:cf37d8c6ef1a48829443e8ba5227b44236d7fcaf7647caa3178a4ff9f7a5be05", size = 8067954 }, + { url = "https://files.pythonhosted.org/packages/3b/c1/23cfb566a74c696a3b338d8955c549900d18fe2b898b6e94d682ca21e7c2/matplotlib-3.10.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9f2efccc8dcf2b86fc4ee849eea5dcaecedd0773b30f47980dc0cbeabf26ec84", size = 8180318 }, + { url = "https://files.pythonhosted.org/packages/6c/0c/02f1c3b66b30da9ee343c343acbb6251bef5b01d34fad732446eaadcd108/matplotlib-3.10.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3ddbba06a6c126e3301c3d272a99dcbe7f6c24c14024e80307ff03791a5f294e", size = 8051132 }, + { url = "https://files.pythonhosted.org/packages/b4/ab/8db1a5ac9b3a7352fb914133001dae889f9fcecb3146541be46bed41339c/matplotlib-3.10.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:748302b33ae9326995b238f606e9ed840bf5886ebafcb233775d946aa8107a15", size = 8457633 }, + { url = "https://files.pythonhosted.org/packages/f5/64/41c4367bcaecbc03ef0d2a3ecee58a7065d0a36ae1aa817fe573a2da66d4/matplotlib-3.10.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a80fcccbef63302c0efd78042ea3c2436104c5b1a4d3ae20f864593696364ac7", size = 8601031 }, + { url = "https://files.pythonhosted.org/packages/12/6f/6cc79e9e5ab89d13ed64da28898e40fe5b105a9ab9c98f83abd24e46d7d7/matplotlib-3.10.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:55e46cbfe1f8586adb34f7587c3e4f7dedc59d5226719faf6cb54fc24f2fd52d", size = 9406988 }, + { url = "https://files.pythonhosted.org/packages/b1/0f/eed564407bd4d935ffabf561ed31099ed609e19287409a27b6d336848653/matplotlib-3.10.3-cp313-cp313-win_amd64.whl", hash = "sha256:151d89cb8d33cb23345cd12490c76fd5d18a56581a16d950b48c6ff19bb2ab93", size = 8068034 }, + { url = "https://files.pythonhosted.org/packages/3e/e5/2f14791ff69b12b09e9975e1d116d9578ac684460860ce542c2588cb7a1c/matplotlib-3.10.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:c26dd9834e74d164d06433dc7be5d75a1e9890b926b3e57e74fa446e1a62c3e2", size = 8218223 }, + { url = "https://files.pythonhosted.org/packages/5c/08/30a94afd828b6e02d0a52cae4a29d6e9ccfcf4c8b56cc28b021d3588873e/matplotlib-3.10.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:24853dad5b8c84c8c2390fc31ce4858b6df504156893292ce8092d190ef8151d", size = 8094985 }, + { url = "https://files.pythonhosted.org/packages/89/44/f3bc6b53066c889d7a1a3ea8094c13af6a667c5ca6220ec60ecceec2dabe/matplotlib-3.10.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68f7878214d369d7d4215e2a9075fef743be38fa401d32e6020bab2dfabaa566", size = 8483109 }, + { url = "https://files.pythonhosted.org/packages/ba/c7/473bc559beec08ebee9f86ca77a844b65747e1a6c2691e8c92e40b9f42a8/matplotlib-3.10.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6929fc618cb6db9cb75086f73b3219bbb25920cb24cee2ea7a12b04971a4158", size = 8618082 }, + { url = "https://files.pythonhosted.org/packages/d8/e9/6ce8edd264c8819e37bbed8172e0ccdc7107fe86999b76ab5752276357a4/matplotlib-3.10.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6c7818292a5cc372a2dc4c795e5c356942eb8350b98ef913f7fda51fe175ac5d", size = 9413699 }, + { url = "https://files.pythonhosted.org/packages/1b/92/9a45c91089c3cf690b5badd4be81e392ff086ccca8a1d4e3a08463d8a966/matplotlib-3.10.3-cp313-cp313t-win_amd64.whl", hash = "sha256:4f23ffe95c5667ef8a2b56eea9b53db7f43910fa4a2d5472ae0f72b64deab4d5", size = 8139044 }, +] + +[[package]] +name = "matplotlib-inline" +version = "0.1.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/99/5b/a36a337438a14116b16480db471ad061c36c3694df7c2084a0da7ba538b7/matplotlib_inline-0.1.7.tar.gz", hash = "sha256:8423b23ec666be3d16e16b60bdd8ac4e86e840ebd1dd11a30b9f117f2fa0ab90", size = 8159 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8f/8e/9ad090d3553c280a8060fbf6e24dc1c0c29704ee7d1c372f0c174aa59285/matplotlib_inline-0.1.7-py3-none-any.whl", hash = "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca", size = 9899 }, +] + +[[package]] +name = "monai" +version = "1.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, + { name = "torch" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a3/61/dc84aca9934c823dbbb46d6f5b4b09f461afa744ea092b66c591e033cff9/monai-1.5.0.tar.gz", hash = "sha256:8c5e4555839812fe060e13da34d9b3342d750d8ad5934f1b79f9b08eb6b35d64", size = 1676883 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/96/f8ede335fa3501c57ec67163ef4d113427effc5ccfc8907c0bb58abe5e2d/monai-1.5.0-py3-none-any.whl", hash = "sha256:93259cfa8b68fbf006dea7c78376a46ce38f369bf8b20b36f74ab1d3f484d37b", size = 2659521 }, +] + +[[package]] +name = "mpmath" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198 }, +] + +[[package]] +name = "multidict" +version = "6.4.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/91/2f/a3470242707058fe856fe59241eee5635d79087100b7042a867368863a27/multidict-6.4.4.tar.gz", hash = "sha256:69ee9e6ba214b5245031b76233dd95408a0fd57fdb019ddcc1ead4790932a8e8", size = 90183 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/19/1b/4c6e638195851524a63972c5773c7737bea7e47b1ba402186a37773acee2/multidict-6.4.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4f5f29794ac0e73d2a06ac03fd18870adc0135a9d384f4a306a951188ed02f95", size = 65515 }, + { url = "https://files.pythonhosted.org/packages/25/d5/10e6bca9a44b8af3c7f920743e5fc0c2bcf8c11bf7a295d4cfe00b08fb46/multidict-6.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c04157266344158ebd57b7120d9b0b35812285d26d0e78193e17ef57bfe2979a", size = 38609 }, + { url = "https://files.pythonhosted.org/packages/26/b4/91fead447ccff56247edc7f0535fbf140733ae25187a33621771ee598a18/multidict-6.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bb61ffd3ab8310d93427e460f565322c44ef12769f51f77277b4abad7b6f7223", size = 37871 }, + { url = "https://files.pythonhosted.org/packages/3b/37/cbc977cae59277e99d15bbda84cc53b5e0c4929ffd91d958347200a42ad0/multidict-6.4.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e0ba18a9afd495f17c351d08ebbc4284e9c9f7971d715f196b79636a4d0de44", size = 226661 }, + { url = "https://files.pythonhosted.org/packages/15/cd/7e0b57fbd4dc2fc105169c4ecce5be1a63970f23bb4ec8c721b67e11953d/multidict-6.4.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:9faf1b1dcaadf9f900d23a0e6d6c8eadd6a95795a0e57fcca73acce0eb912065", size = 223422 }, + { url = "https://files.pythonhosted.org/packages/f1/01/1de268da121bac9f93242e30cd3286f6a819e5f0b8896511162d6ed4bf8d/multidict-6.4.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a4d1cb1327c6082c4fce4e2a438483390964c02213bc6b8d782cf782c9b1471f", size = 235447 }, + { url = "https://files.pythonhosted.org/packages/d2/8c/8b9a5e4aaaf4f2de14e86181a3a3d7b105077f668b6a06f043ec794f684c/multidict-6.4.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:941f1bec2f5dbd51feeb40aea654c2747f811ab01bdd3422a48a4e4576b7d76a", size = 231455 }, + { url = "https://files.pythonhosted.org/packages/35/db/e1817dcbaa10b319c412769cf999b1016890849245d38905b73e9c286862/multidict-6.4.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e5f8a146184da7ea12910a4cec51ef85e44f6268467fb489c3caf0cd512f29c2", size = 223666 }, + { url = "https://files.pythonhosted.org/packages/4a/e1/66e8579290ade8a00e0126b3d9a93029033ffd84f0e697d457ed1814d0fc/multidict-6.4.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:232b7237e57ec3c09be97206bfb83a0aa1c5d7d377faa019c68a210fa35831f1", size = 217392 }, + { url = "https://files.pythonhosted.org/packages/7b/6f/f8639326069c24a48c7747c2a5485d37847e142a3f741ff3340c88060a9a/multidict-6.4.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:55ae0721c1513e5e3210bca4fc98456b980b0c2c016679d3d723119b6b202c42", size = 228969 }, + { url = "https://files.pythonhosted.org/packages/d2/c3/3d58182f76b960eeade51c89fcdce450f93379340457a328e132e2f8f9ed/multidict-6.4.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:51d662c072579f63137919d7bb8fc250655ce79f00c82ecf11cab678f335062e", size = 217433 }, + { url = "https://files.pythonhosted.org/packages/e1/4b/f31a562906f3bd375f3d0e83ce314e4a660c01b16c2923e8229b53fba5d7/multidict-6.4.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:0e05c39962baa0bb19a6b210e9b1422c35c093b651d64246b6c2e1a7e242d9fd", size = 225418 }, + { url = "https://files.pythonhosted.org/packages/99/89/78bb95c89c496d64b5798434a3deee21996114d4d2c28dd65850bf3a691e/multidict-6.4.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:d5b1cc3ab8c31d9ebf0faa6e3540fb91257590da330ffe6d2393d4208e638925", size = 235042 }, + { url = "https://files.pythonhosted.org/packages/74/91/8780a6e5885a8770442a8f80db86a0887c4becca0e5a2282ba2cae702bc4/multidict-6.4.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:93ec84488a384cd7b8a29c2c7f467137d8a73f6fe38bb810ecf29d1ade011a7c", size = 230280 }, + { url = "https://files.pythonhosted.org/packages/68/c1/fcf69cabd542eb6f4b892469e033567ee6991d361d77abdc55e3a0f48349/multidict-6.4.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b308402608493638763abc95f9dc0030bbd6ac6aff784512e8ac3da73a88af08", size = 223322 }, + { url = "https://files.pythonhosted.org/packages/b8/85/5b80bf4b83d8141bd763e1d99142a9cdfd0db83f0739b4797172a4508014/multidict-6.4.4-cp311-cp311-win32.whl", hash = "sha256:343892a27d1a04d6ae455ecece12904d242d299ada01633d94c4f431d68a8c49", size = 35070 }, + { url = "https://files.pythonhosted.org/packages/09/66/0bed198ffd590ab86e001f7fa46b740d58cf8ff98c2f254e4a36bf8861ad/multidict-6.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:73484a94f55359780c0f458bbd3c39cb9cf9c182552177d2136e828269dee529", size = 38667 }, + { url = "https://files.pythonhosted.org/packages/d2/b5/5675377da23d60875fe7dae6be841787755878e315e2f517235f22f59e18/multidict-6.4.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:dc388f75a1c00000824bf28b7633e40854f4127ede80512b44c3cfeeea1839a2", size = 64293 }, + { url = "https://files.pythonhosted.org/packages/34/a7/be384a482754bb8c95d2bbe91717bf7ccce6dc38c18569997a11f95aa554/multidict-6.4.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:98af87593a666f739d9dba5d0ae86e01b0e1a9cfcd2e30d2d361fbbbd1a9162d", size = 38096 }, + { url = "https://files.pythonhosted.org/packages/66/6d/d59854bb4352306145bdfd1704d210731c1bb2c890bfee31fb7bbc1c4c7f/multidict-6.4.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:aff4cafea2d120327d55eadd6b7f1136a8e5a0ecf6fb3b6863e8aca32cd8e50a", size = 37214 }, + { url = "https://files.pythonhosted.org/packages/99/e0/c29d9d462d7cfc5fc8f9bf24f9c6843b40e953c0b55e04eba2ad2cf54fba/multidict-6.4.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:169c4ba7858176b797fe551d6e99040c531c775d2d57b31bcf4de6d7a669847f", size = 224686 }, + { url = "https://files.pythonhosted.org/packages/dc/4a/da99398d7fd8210d9de068f9a1b5f96dfaf67d51e3f2521f17cba4ee1012/multidict-6.4.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b9eb4c59c54421a32b3273d4239865cb14ead53a606db066d7130ac80cc8ec93", size = 231061 }, + { url = "https://files.pythonhosted.org/packages/21/f5/ac11add39a0f447ac89353e6ca46666847051103649831c08a2800a14455/multidict-6.4.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7cf3bd54c56aa16fdb40028d545eaa8d051402b61533c21e84046e05513d5780", size = 232412 }, + { url = "https://files.pythonhosted.org/packages/d9/11/4b551e2110cded705a3c13a1d4b6a11f73891eb5a1c449f1b2b6259e58a6/multidict-6.4.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f682c42003c7264134bfe886376299db4cc0c6cd06a3295b41b347044bcb5482", size = 231563 }, + { url = "https://files.pythonhosted.org/packages/4c/02/751530c19e78fe73b24c3da66618eda0aa0d7f6e7aa512e46483de6be210/multidict-6.4.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a920f9cf2abdf6e493c519492d892c362007f113c94da4c239ae88429835bad1", size = 223811 }, + { url = "https://files.pythonhosted.org/packages/c7/cb/2be8a214643056289e51ca356026c7b2ce7225373e7a1f8c8715efee8988/multidict-6.4.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:530d86827a2df6504526106b4c104ba19044594f8722d3e87714e847c74a0275", size = 216524 }, + { url = "https://files.pythonhosted.org/packages/19/f3/6d5011ec375c09081f5250af58de85f172bfcaafebff286d8089243c4bd4/multidict-6.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ecde56ea2439b96ed8a8d826b50c57364612ddac0438c39e473fafad7ae1c23b", size = 229012 }, + { url = "https://files.pythonhosted.org/packages/67/9c/ca510785df5cf0eaf5b2a8132d7d04c1ce058dcf2c16233e596ce37a7f8e/multidict-6.4.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:dc8c9736d8574b560634775ac0def6bdc1661fc63fa27ffdfc7264c565bcb4f2", size = 226765 }, + { url = "https://files.pythonhosted.org/packages/36/c8/ca86019994e92a0f11e642bda31265854e6ea7b235642f0477e8c2e25c1f/multidict-6.4.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:7f3d3b3c34867579ea47cbd6c1f2ce23fbfd20a273b6f9e3177e256584f1eacc", size = 222888 }, + { url = "https://files.pythonhosted.org/packages/c6/67/bc25a8e8bd522935379066950ec4e2277f9b236162a73548a2576d4b9587/multidict-6.4.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:87a728af265e08f96b6318ebe3c0f68b9335131f461efab2fc64cc84a44aa6ed", size = 234041 }, + { url = "https://files.pythonhosted.org/packages/f1/a0/70c4c2d12857fccbe607b334b7ee28b6b5326c322ca8f73ee54e70d76484/multidict-6.4.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9f193eeda1857f8e8d3079a4abd258f42ef4a4bc87388452ed1e1c4d2b0c8740", size = 231046 }, + { url = "https://files.pythonhosted.org/packages/c1/0f/52954601d02d39742aab01d6b92f53c1dd38b2392248154c50797b4df7f1/multidict-6.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:be06e73c06415199200e9a2324a11252a3d62030319919cde5e6950ffeccf72e", size = 227106 }, + { url = "https://files.pythonhosted.org/packages/af/24/679d83ec4379402d28721790dce818e5d6b9f94ce1323a556fb17fa9996c/multidict-6.4.4-cp312-cp312-win32.whl", hash = "sha256:622f26ea6a7e19b7c48dd9228071f571b2fbbd57a8cd71c061e848f281550e6b", size = 35351 }, + { url = "https://files.pythonhosted.org/packages/52/ef/40d98bc5f986f61565f9b345f102409534e29da86a6454eb6b7c00225a13/multidict-6.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:5e2bcda30d5009996ff439e02a9f2b5c3d64a20151d34898c000a6281faa3781", size = 38791 }, + { url = "https://files.pythonhosted.org/packages/df/2a/e166d2ffbf4b10131b2d5b0e458f7cee7d986661caceae0de8753042d4b2/multidict-6.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:82ffabefc8d84c2742ad19c37f02cde5ec2a1ee172d19944d380f920a340e4b9", size = 64123 }, + { url = "https://files.pythonhosted.org/packages/8c/96/e200e379ae5b6f95cbae472e0199ea98913f03d8c9a709f42612a432932c/multidict-6.4.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6a2f58a66fe2c22615ad26156354005391e26a2f3721c3621504cd87c1ea87bf", size = 38049 }, + { url = "https://files.pythonhosted.org/packages/75/fb/47afd17b83f6a8c7fa863c6d23ac5ba6a0e6145ed8a6bcc8da20b2b2c1d2/multidict-6.4.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5883d6ee0fd9d8a48e9174df47540b7545909841ac82354c7ae4cbe9952603bd", size = 37078 }, + { url = "https://files.pythonhosted.org/packages/fa/70/1af3143000eddfb19fd5ca5e78393985ed988ac493bb859800fe0914041f/multidict-6.4.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9abcf56a9511653fa1d052bfc55fbe53dbee8f34e68bd6a5a038731b0ca42d15", size = 224097 }, + { url = "https://files.pythonhosted.org/packages/b1/39/d570c62b53d4fba844e0378ffbcd02ac25ca423d3235047013ba2f6f60f8/multidict-6.4.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6ed5ae5605d4ad5a049fad2a28bb7193400700ce2f4ae484ab702d1e3749c3f9", size = 230768 }, + { url = "https://files.pythonhosted.org/packages/fd/f8/ed88f2c4d06f752b015933055eb291d9bc184936903752c66f68fb3c95a7/multidict-6.4.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bbfcb60396f9bcfa63e017a180c3105b8c123a63e9d1428a36544e7d37ca9e20", size = 231331 }, + { url = "https://files.pythonhosted.org/packages/9c/6f/8e07cffa32f483ab887b0d56bbd8747ac2c1acd00dc0af6fcf265f4a121e/multidict-6.4.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b0f1987787f5f1e2076b59692352ab29a955b09ccc433c1f6b8e8e18666f608b", size = 230169 }, + { url = "https://files.pythonhosted.org/packages/e6/2b/5dcf173be15e42f330110875a2668ddfc208afc4229097312212dc9c1236/multidict-6.4.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1d0121ccce8c812047d8d43d691a1ad7641f72c4f730474878a5aeae1b8ead8c", size = 222947 }, + { url = "https://files.pythonhosted.org/packages/39/75/4ddcbcebe5ebcd6faa770b629260d15840a5fc07ce8ad295a32e14993726/multidict-6.4.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:83ec4967114295b8afd120a8eec579920c882831a3e4c3331d591a8e5bfbbc0f", size = 215761 }, + { url = "https://files.pythonhosted.org/packages/6a/c9/55e998ae45ff15c5608e384206aa71a11e1b7f48b64d166db400b14a3433/multidict-6.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:995f985e2e268deaf17867801b859a282e0448633f1310e3704b30616d269d69", size = 227605 }, + { url = "https://files.pythonhosted.org/packages/04/49/c2404eac74497503c77071bd2e6f88c7e94092b8a07601536b8dbe99be50/multidict-6.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:d832c608f94b9f92a0ec8b7e949be7792a642b6e535fcf32f3e28fab69eeb046", size = 226144 }, + { url = "https://files.pythonhosted.org/packages/62/c5/0cd0c3c6f18864c40846aa2252cd69d308699cb163e1c0d989ca301684da/multidict-6.4.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d21c1212171cf7da703c5b0b7a0e85be23b720818aef502ad187d627316d5645", size = 221100 }, + { url = "https://files.pythonhosted.org/packages/71/7b/f2f3887bea71739a046d601ef10e689528d4f911d84da873b6be9194ffea/multidict-6.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:cbebaa076aaecad3d4bb4c008ecc73b09274c952cf6a1b78ccfd689e51f5a5b0", size = 232731 }, + { url = "https://files.pythonhosted.org/packages/e5/b3/d9de808349df97fa75ec1372758701b5800ebad3c46ae377ad63058fbcc6/multidict-6.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:c93a6fb06cc8e5d3628b2b5fda215a5db01e8f08fc15fadd65662d9b857acbe4", size = 229637 }, + { url = "https://files.pythonhosted.org/packages/5e/57/13207c16b615eb4f1745b44806a96026ef8e1b694008a58226c2d8f5f0a5/multidict-6.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8cd8f81f1310182362fb0c7898145ea9c9b08a71081c5963b40ee3e3cac589b1", size = 225594 }, + { url = "https://files.pythonhosted.org/packages/3a/e4/d23bec2f70221604f5565000632c305fc8f25ba953e8ce2d8a18842b9841/multidict-6.4.4-cp313-cp313-win32.whl", hash = "sha256:3e9f1cd61a0ab857154205fb0b1f3d3ace88d27ebd1409ab7af5096e409614cd", size = 35359 }, + { url = "https://files.pythonhosted.org/packages/a7/7a/cfe1a47632be861b627f46f642c1d031704cc1c0f5c0efbde2ad44aa34bd/multidict-6.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:8ffb40b74400e4455785c2fa37eba434269149ec525fc8329858c862e4b35373", size = 38903 }, + { url = "https://files.pythonhosted.org/packages/68/7b/15c259b0ab49938a0a1c8f3188572802704a779ddb294edc1b2a72252e7c/multidict-6.4.4-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:6a602151dbf177be2450ef38966f4be3467d41a86c6a845070d12e17c858a156", size = 68895 }, + { url = "https://files.pythonhosted.org/packages/f1/7d/168b5b822bccd88142e0a3ce985858fea612404edd228698f5af691020c9/multidict-6.4.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0d2b9712211b860d123815a80b859075d86a4d54787e247d7fbee9db6832cf1c", size = 40183 }, + { url = "https://files.pythonhosted.org/packages/e0/b7/d4b8d98eb850ef28a4922ba508c31d90715fd9b9da3801a30cea2967130b/multidict-6.4.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:d2fa86af59f8fc1972e121ade052145f6da22758f6996a197d69bb52f8204e7e", size = 39592 }, + { url = "https://files.pythonhosted.org/packages/18/28/a554678898a19583548e742080cf55d169733baf57efc48c2f0273a08583/multidict-6.4.4-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50855d03e9e4d66eab6947ba688ffb714616f985838077bc4b490e769e48da51", size = 226071 }, + { url = "https://files.pythonhosted.org/packages/ee/dc/7ba6c789d05c310e294f85329efac1bf5b450338d2542498db1491a264df/multidict-6.4.4-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:5bce06b83be23225be1905dcdb6b789064fae92499fbc458f59a8c0e68718601", size = 222597 }, + { url = "https://files.pythonhosted.org/packages/24/4f/34eadbbf401b03768dba439be0fb94b0d187facae9142821a3d5599ccb3b/multidict-6.4.4-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:66ed0731f8e5dfd8369a883b6e564aca085fb9289aacabd9decd70568b9a30de", size = 228253 }, + { url = "https://files.pythonhosted.org/packages/c0/e6/493225a3cdb0d8d80d43a94503fc313536a07dae54a3f030d279e629a2bc/multidict-6.4.4-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:329ae97fc2f56f44d91bc47fe0972b1f52d21c4b7a2ac97040da02577e2daca2", size = 226146 }, + { url = "https://files.pythonhosted.org/packages/2f/70/e411a7254dc3bff6f7e6e004303b1b0591358e9f0b7c08639941e0de8bd6/multidict-6.4.4-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c27e5dcf520923d6474d98b96749e6805f7677e93aaaf62656005b8643f907ab", size = 220585 }, + { url = "https://files.pythonhosted.org/packages/08/8f/beb3ae7406a619100d2b1fb0022c3bb55a8225ab53c5663648ba50dfcd56/multidict-6.4.4-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:058cc59b9e9b143cc56715e59e22941a5d868c322242278d28123a5d09cdf6b0", size = 212080 }, + { url = "https://files.pythonhosted.org/packages/9c/ec/355124e9d3d01cf8edb072fd14947220f357e1c5bc79c88dff89297e9342/multidict-6.4.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:69133376bc9a03f8c47343d33f91f74a99c339e8b58cea90433d8e24bb298031", size = 226558 }, + { url = "https://files.pythonhosted.org/packages/fd/22/d2b95cbebbc2ada3be3812ea9287dcc9712d7f1a012fad041770afddb2ad/multidict-6.4.4-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:d6b15c55721b1b115c5ba178c77104123745b1417527ad9641a4c5e2047450f0", size = 212168 }, + { url = "https://files.pythonhosted.org/packages/4d/c5/62bfc0b2f9ce88326dbe7179f9824a939c6c7775b23b95de777267b9725c/multidict-6.4.4-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:a887b77f51d3d41e6e1a63cf3bc7ddf24de5939d9ff69441387dfefa58ac2e26", size = 217970 }, + { url = "https://files.pythonhosted.org/packages/79/74/977cea1aadc43ff1c75d23bd5bc4768a8fac98c14e5878d6ee8d6bab743c/multidict-6.4.4-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:632a3bf8f1787f7ef7d3c2f68a7bde5be2f702906f8b5842ad6da9d974d0aab3", size = 226980 }, + { url = "https://files.pythonhosted.org/packages/48/fc/cc4a1a2049df2eb84006607dc428ff237af38e0fcecfdb8a29ca47b1566c/multidict-6.4.4-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:a145c550900deb7540973c5cdb183b0d24bed6b80bf7bddf33ed8f569082535e", size = 220641 }, + { url = "https://files.pythonhosted.org/packages/3b/6a/a7444d113ab918701988d4abdde373dbdfd2def7bd647207e2bf645c7eac/multidict-6.4.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:cc5d83c6619ca5c9672cb78b39ed8542f1975a803dee2cda114ff73cbb076edd", size = 221728 }, + { url = "https://files.pythonhosted.org/packages/2b/b0/fdf4c73ad1c55e0f4dbbf2aa59dd37037334091f9a4961646d2b7ac91a86/multidict-6.4.4-cp313-cp313t-win32.whl", hash = "sha256:3312f63261b9df49be9d57aaa6abf53a6ad96d93b24f9cc16cf979956355ce6e", size = 41913 }, + { url = "https://files.pythonhosted.org/packages/8e/92/27989ecca97e542c0d01d05a98a5ae12198a243a9ee12563a0313291511f/multidict-6.4.4-cp313-cp313t-win_amd64.whl", hash = "sha256:ba852168d814b2c73333073e1c7116d9395bea69575a01b0b3c89d2d5a87c8fb", size = 46112 }, + { url = "https://files.pythonhosted.org/packages/84/5d/e17845bb0fa76334477d5de38654d27946d5b5d3695443987a094a71b440/multidict-6.4.4-py3-none-any.whl", hash = "sha256:bd4557071b561a8b3b6075c3ce93cf9bfb6182cb241805c3d66ced3b75eff4ac", size = 10481 }, +] + +[[package]] +name = "musk" +version = "1.0.0" +source = { git = "https://github.com/lilab-stanford/MUSK.git?rev=e1699c27687f44bbf6d4adfcbb2abe89795d347f#e1699c27687f44bbf6d4adfcbb2abe89795d347f" } + +[[package]] +name = "nest-asyncio" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/83/f8/51569ac65d696c8ecbee95938f89d4abf00f47d58d48f6fbabfe8f0baefe/nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe", size = 7418 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/c4/c2971a3ba4c6103a3d10c4b0f24f461ddc027f0f09763220cf35ca1401b3/nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c", size = 5195 }, +] + +[[package]] +name = "networkx" +version = "3.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6c/4f/ccdb8ad3a38e583f214547fd2f7ff1fc160c43a75af88e6aec213404b96a/networkx-3.5.tar.gz", hash = "sha256:d4c6f9cf81f52d69230866796b82afbccdec3db7ae4fbd1b65ea750feed50037", size = 2471065 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/eb/8d/776adee7bbf76365fdd7f2552710282c79a4ead5d2a46408c9043a2b70ba/networkx-3.5-py3-none-any.whl", hash = "sha256:0030d386a9a06dee3565298b4a734b68589749a544acbb6c412dc9e2489ec6ec", size = 2034406 }, +] + +[[package]] +name = "ninja" +version = "1.11.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/37/2c/d717d13a413d6f7579cdaa1e28e6e2c98de95461549b08d311c8a5bf4c51/ninja-1.11.1.1.tar.gz", hash = "sha256:9d793b08dd857e38d0b6ffe9e6b7145d7c485a42dcfea04905ca0cdb6017cc3c", size = 132392 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3d/6e/04ed11bb244039908f6f212cb5f3e97933e238655248e4ce307c1687ba1f/ninja-1.11.1.1-py2.py3-none-macosx_10_9_universal2.macosx_10_9_x86_64.macosx_11_0_arm64.macosx_11_0_universal2.whl", hash = "sha256:376889c76d87b95b5719fdd61dd7db193aa7fd4432e5d52d2e44e4c497bdbbee", size = 270611 }, + { url = "https://files.pythonhosted.org/packages/2c/52/0e5423311eb9939b6f9354059a6d88a6211eb4fa1c7a4ef303ecee1c1fe0/ninja-1.11.1.1-py2.py3-none-manylinux1_i686.manylinux_2_5_i686.whl", hash = "sha256:ecf80cf5afd09f14dcceff28cb3f11dc90fb97c999c89307aea435889cb66877", size = 324256 }, + { url = "https://files.pythonhosted.org/packages/6d/92/8d7aebd4430ab5ff65df2bfee6d5745f95c004284db2d8ca76dcbfd9de47/ninja-1.11.1.1-py2.py3-none-manylinux1_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:84502ec98f02a037a169c4b0d5d86075eaf6afc55e1879003d6cab51ced2ea4b", size = 307194 }, + { url = "https://files.pythonhosted.org/packages/01/c8/96424839fd127b4492229acf50763ed9940d864ca35d17d151934aef1f6f/ninja-1.11.1.1-py2.py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:73b93c14046447c7c5cc892433d4fae65d6364bec6685411cb97a8bcf815f93a", size = 155643 }, + { url = "https://files.pythonhosted.org/packages/6b/fa/5ca8e65a98cdb9a71d4f1e38cac7bd757bbb9555a5aef5a4d293aa890e5c/ninja-1.11.1.1-py2.py3-none-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:18302d96a5467ea98b68e1cae1ae4b4fb2b2a56a82b955193c637557c7273dbd", size = 179538 }, + { url = "https://files.pythonhosted.org/packages/45/ef/60086f02cbc6882da00a02c81d645cefd8d2d65b01fade41b873d8dd85a2/ninja-1.11.1.1-py2.py3-none-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:aad34a70ef15b12519946c5633344bc775a7656d789d9ed5fdb0d456383716ef", size = 156217 }, + { url = "https://files.pythonhosted.org/packages/1c/00/2fd13ac6aafdb566f00d6b541101fca54e58ae58bf96c00f9780df019607/ninja-1.11.1.1-py2.py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:d491fc8d89cdcb416107c349ad1e3a735d4c4af5e1cb8f5f727baca6350fdaea", size = 372069 }, + { url = "https://files.pythonhosted.org/packages/ad/5d/6e97c8a25167d4867694c7fb0b9bdbc9b096d6479c8e56c5bd41b49613f6/ninja-1.11.1.1-py2.py3-none-musllinux_1_1_i686.whl", hash = "sha256:7563ce1d9fe6ed5af0b8dd9ab4a214bf4ff1f2f6fd6dc29f480981f0f8b8b249", size = 418859 }, + { url = "https://files.pythonhosted.org/packages/43/78/34af88d753389a9412438d16142c77e587e0d69152faf0bbf99701063dd8/ninja-1.11.1.1-py2.py3-none-musllinux_1_1_ppc64le.whl", hash = "sha256:9df724344202b83018abb45cb1efc22efd337a1496514e7e6b3b59655be85205", size = 419782 }, + { url = "https://files.pythonhosted.org/packages/3b/74/de0633f8bced3b188942fca64a950e8f2206c60c10c97af465b356ae9b25/ninja-1.11.1.1-py2.py3-none-musllinux_1_1_s390x.whl", hash = "sha256:3e0f9be5bb20d74d58c66cc1c414c3e6aeb45c35b0d0e41e8d739c2c0d57784f", size = 415476 }, + { url = "https://files.pythonhosted.org/packages/9a/f3/3e4a56ff77739d1582749b93497bdebf11e003fbc7a66363ef6c772ebd0a/ninja-1.11.1.1-py2.py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:76482ba746a2618eecf89d5253c0d1e4f1da1270d41e9f54dfbd91831b0f6885", size = 379229 }, + { url = "https://files.pythonhosted.org/packages/c5/ee/53df34fcc9c0b1db62b2f2e2c848e28d9354e1c7f0dce029ee50b16ca157/ninja-1.11.1.1-py2.py3-none-win32.whl", hash = "sha256:fa2ba9d74acfdfbfbcf06fad1b8282de8a7a8c481d9dee45c859a8c93fcc1082", size = 265049 }, + { url = "https://files.pythonhosted.org/packages/b6/2f/a3bc50fa63fc4fe9348e15b53dc8c87febfd4e0c660fcf250c4b19a3aa3b/ninja-1.11.1.1-py2.py3-none-win_amd64.whl", hash = "sha256:95da904130bfa02ea74ff9c0116b4ad266174fafb1c707aa50212bc7859aebf1", size = 312958 }, + { url = "https://files.pythonhosted.org/packages/73/2a/f5b7b3b7ecd5cf4e31375580bf5c6a01a328ed1ebdfff90fab463e3f4bc7/ninja-1.11.1.1-py2.py3-none-win_arm64.whl", hash = "sha256:185e0641bde601e53841525c4196278e9aaf4463758da6dd1e752c0a0f54136a", size = 272686 }, +] + +[[package]] +name = "nodeenv" +version = "1.9.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/16/fc88b08840de0e0a72a2f9d8c6bae36be573e475a6326ae854bcc549fc45/nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f", size = 47437 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314 }, +] + +[[package]] +name = "numexpr" +version = "2.11.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d2/8f/2cc977e91adbfbcdb6b49fdb9147e1d1c7566eb2c0c1e737e9a47020b5ca/numexpr-2.11.0.tar.gz", hash = "sha256:75b2c01a4eda2e7c357bc67a3f5c3dd76506c15b5fd4dc42845ef2e182181bad", size = 108960 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d8/d1/1cf8137990b3f3d445556ed63b9bc347aec39bde8c41146b02d3b35c1adc/numexpr-2.11.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:450eba3c93c3e3e8070566ad8d70590949d6e574b1c960bf68edd789811e7da8", size = 147535 }, + { url = "https://files.pythonhosted.org/packages/b6/5e/bac7649d043f47c7c14c797efe60dbd19476468a149399cd706fe2e47f8c/numexpr-2.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f0eb88dbac8a7e61ee433006d0ddfd6eb921f5c6c224d1b50855bc98fb304c44", size = 136710 }, + { url = "https://files.pythonhosted.org/packages/1b/9f/c88fc34d82d23c66ea0b78b00a1fb3b64048e0f7ac7791b2cd0d2a4ce14d/numexpr-2.11.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a194e3684b3553ea199c3f4837f422a521c7e2f0cce13527adc3a6b4049f9e7c", size = 411169 }, + { url = "https://files.pythonhosted.org/packages/e4/8d/4d78dad430b41d836146f9e6f545f5c4f7d1972a6aa427d8570ab232bf16/numexpr-2.11.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f677668ab2bb2452fee955af3702fbb3b71919e61e4520762b1e5f54af59c0d8", size = 401671 }, + { url = "https://files.pythonhosted.org/packages/83/1c/414670eb41a82b78bd09769a4f5fb49a934f9b3990957f02c833637a511e/numexpr-2.11.0-cp311-cp311-win32.whl", hash = "sha256:7d9e76a77c9644fbd60da3984e516ead5b84817748c2da92515cd36f1941a04d", size = 153159 }, + { url = "https://files.pythonhosted.org/packages/0c/97/8d00ca9b36f3ac68a8fd85e930ab0c9448d8c9ca7ce195ee75c188dabd45/numexpr-2.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:7163b488bfdcd13c300a8407c309e4cee195ef95d07facf5ac2678d66c988805", size = 146224 }, + { url = "https://files.pythonhosted.org/packages/38/45/7a0e5a0b800d92e73825494ac695fa05a52c7fc7088d69a336880136b437/numexpr-2.11.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4229060be866813122385c608bbd3ea48fe0b33e91f2756810d28c1cdbfc98f1", size = 147494 }, + { url = "https://files.pythonhosted.org/packages/74/46/3a26b84e44f4739ec98de0ede4b95b4b8096f721e22d0e97517eeb02017e/numexpr-2.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:097aa8835d32d6ac52f2be543384019b4b134d1fb67998cbfc4271155edfe54a", size = 136832 }, + { url = "https://files.pythonhosted.org/packages/75/05/e3076ff25d4a108b47640c169c0a64811748c43b63d9cc052ea56de1631e/numexpr-2.11.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7f082321c244ff5d0e252071fb2c4fe02063a45934144a1456a5370ca139bec2", size = 412618 }, + { url = "https://files.pythonhosted.org/packages/70/e8/15e0e077a004db0edd530da96c60c948689c888c464ee5d14b82405ebd86/numexpr-2.11.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d7a19435ca3d7dd502b8d8dce643555eb1b6013989e3f7577857289f6db6be16", size = 403363 }, + { url = "https://files.pythonhosted.org/packages/10/14/f22afb3a7ae41d03ba87f62d00fbcfb76389f9cc91b7a82593c39c509318/numexpr-2.11.0-cp312-cp312-win32.whl", hash = "sha256:f326218262c8d8537887cc4bbd613c8409d62f2cac799835c0360e0d9cefaa5c", size = 153307 }, + { url = "https://files.pythonhosted.org/packages/18/70/abc585269424582b3cd6db261e33b2ec96b5d4971da3edb29fc9b62a8926/numexpr-2.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:0a184e5930c77ab91dd9beee4df403b825cd9dfc4e9ba4670d31c9fcb4e2c08e", size = 146337 }, + { url = "https://files.pythonhosted.org/packages/74/63/dbf4fb6c48006d413a82db138d03c3c007d0ed0684f693c4b77196448660/numexpr-2.11.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:eb766218abad05c7c3ddad5367d0ec702d6152cb4a48d9fd56a6cef6abade70c", size = 147495 }, + { url = "https://files.pythonhosted.org/packages/3a/e4/2fbbf5b9121f54722dc4d4dfc75bc0b4e8ee2675f92ec86ee5697aecc53f/numexpr-2.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2036be213a6a1b5ce49acf60de99b911a0f9d174aab7679dde1fae315134f826", size = 136839 }, + { url = "https://files.pythonhosted.org/packages/a8/3f/aa36415919c90f712a11127eaa7c0c8d045768d62a484a29364e4801c383/numexpr-2.11.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:096ec768bee2ef14ac757b4178e3c5f05e5f1cb6cae83b2eea9b4ba3ec1a86dd", size = 416240 }, + { url = "https://files.pythonhosted.org/packages/b9/7d/4911f40d3610fc5557029f0d1f20ef9f571488319567ac4d8ee6d0978ee6/numexpr-2.11.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a1719788a787808c15c9bb98b6ff0c97d64a0e59c1a6ebe36d4ae4d7c5c09b95", size = 406641 }, + { url = "https://files.pythonhosted.org/packages/6f/bc/d00e717e77691c410c6c461d7880b4c498896874316acc0e044d7eafacbf/numexpr-2.11.0-cp313-cp313-win32.whl", hash = "sha256:6b5fdfc86cbf5373ea67d554cc6f08863825ea8e928416bed8d5285e387420c6", size = 153313 }, + { url = "https://files.pythonhosted.org/packages/52/a2/93346789e6d73a76fdb68171904ade25c112f25df363a8f602c6b21bc220/numexpr-2.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:5ff337b36db141a1a0b49f01282783744f49f0d401cc83a512fc5596eb7db5c6", size = 146340 }, + { url = "https://files.pythonhosted.org/packages/0b/20/c0e3aaf3cc4497e5253df2523a55c83b9d316cb5c9d5caaa4a1156cef6e3/numexpr-2.11.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:b9854fa70edbe93242b8bb4840e58d1128c45766d9a70710f05b4f67eb0feb6e", size = 148206 }, + { url = "https://files.pythonhosted.org/packages/de/49/22fd38ac990ba333f25b771305a5ffcd98c771f4d278868661ffb26deac1/numexpr-2.11.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:321736cb98f090ce864b58cc5c37661cb5548e394e0fe24d5f2c7892a89070c3", size = 137573 }, + { url = "https://files.pythonhosted.org/packages/fb/1e/50074e472e9e6bea4fe430869708d9ede333a187d8d0740e70d5a9560aad/numexpr-2.11.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b5cc434eb4a4df2fe442bcc50df114e82ff7aa234657baf873b2c9cf3f851e8e", size = 426674 }, + { url = "https://files.pythonhosted.org/packages/8e/6d/7ccbc72b950653df62d29e2531c811ed80cfff93c927a5bfd86a71edb4da/numexpr-2.11.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:238d19465a272ada3967600fada55e4c6900485aefb42122a78dfcaf2efca65f", size = 416037 }, + { url = "https://files.pythonhosted.org/packages/31/7c/bbccad2734dd4b251cc6bdff8cf5ded18b5383f5a05aa8de7bf02acbb65b/numexpr-2.11.0-cp313-cp313t-win32.whl", hash = "sha256:0db4c2dcad09f9594b45fce794f4b903345195a8c216e252de2aa92884fd81a8", size = 153967 }, + { url = "https://files.pythonhosted.org/packages/75/d7/41287384e413e8d20457d35e264d9c9754e65eb13a988af51ceb7057f61b/numexpr-2.11.0-cp313-cp313t-win_amd64.whl", hash = "sha256:a69b5c02014448a412012752dc46091902d28932c3be0c6e02e73cecceffb700", size = 147207 }, +] + +[[package]] +name = "numpy" +version = "2.2.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/76/21/7d2a95e4bba9dc13d043ee156a356c0a8f0c6309dff6b21b4d71a073b8a8/numpy-2.2.6.tar.gz", hash = "sha256:e29554e2bef54a90aa5cc07da6ce955accb83f21ab5de01a62c8478897b264fd", size = 20276440 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/da/a8/4f83e2aa666a9fbf56d6118faaaf5f1974d456b1823fda0a176eff722839/numpy-2.2.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f9f1adb22318e121c5c69a09142811a201ef17ab257a1e66ca3025065b7f53ae", size = 21176963 }, + { url = "https://files.pythonhosted.org/packages/b3/2b/64e1affc7972decb74c9e29e5649fac940514910960ba25cd9af4488b66c/numpy-2.2.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c820a93b0255bc360f53eca31a0e676fd1101f673dda8da93454a12e23fc5f7a", size = 14406743 }, + { url = "https://files.pythonhosted.org/packages/4a/9f/0121e375000b5e50ffdd8b25bf78d8e1a5aa4cca3f185d41265198c7b834/numpy-2.2.6-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3d70692235e759f260c3d837193090014aebdf026dfd167834bcba43e30c2a42", size = 5352616 }, + { url = "https://files.pythonhosted.org/packages/31/0d/b48c405c91693635fbe2dcd7bc84a33a602add5f63286e024d3b6741411c/numpy-2.2.6-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:481b49095335f8eed42e39e8041327c05b0f6f4780488f61286ed3c01368d491", size = 6889579 }, + { url = "https://files.pythonhosted.org/packages/52/b8/7f0554d49b565d0171eab6e99001846882000883998e7b7d9f0d98b1f934/numpy-2.2.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b64d8d4d17135e00c8e346e0a738deb17e754230d7e0810ac5012750bbd85a5a", size = 14312005 }, + { url = "https://files.pythonhosted.org/packages/b3/dd/2238b898e51bd6d389b7389ffb20d7f4c10066d80351187ec8e303a5a475/numpy-2.2.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba10f8411898fc418a521833e014a77d3ca01c15b0c6cdcce6a0d2897e6dbbdf", size = 16821570 }, + { url = "https://files.pythonhosted.org/packages/83/6c/44d0325722cf644f191042bf47eedad61c1e6df2432ed65cbe28509d404e/numpy-2.2.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bd48227a919f1bafbdda0583705e547892342c26fb127219d60a5c36882609d1", size = 15818548 }, + { url = "https://files.pythonhosted.org/packages/ae/9d/81e8216030ce66be25279098789b665d49ff19eef08bfa8cb96d4957f422/numpy-2.2.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9551a499bf125c1d4f9e250377c1ee2eddd02e01eac6644c080162c0c51778ab", size = 18620521 }, + { url = "https://files.pythonhosted.org/packages/6a/fd/e19617b9530b031db51b0926eed5345ce8ddc669bb3bc0044b23e275ebe8/numpy-2.2.6-cp311-cp311-win32.whl", hash = "sha256:0678000bb9ac1475cd454c6b8c799206af8107e310843532b04d49649c717a47", size = 6525866 }, + { url = "https://files.pythonhosted.org/packages/31/0a/f354fb7176b81747d870f7991dc763e157a934c717b67b58456bc63da3df/numpy-2.2.6-cp311-cp311-win_amd64.whl", hash = "sha256:e8213002e427c69c45a52bbd94163084025f533a55a59d6f9c5b820774ef3303", size = 12907455 }, + { url = "https://files.pythonhosted.org/packages/82/5d/c00588b6cf18e1da539b45d3598d3557084990dcc4331960c15ee776ee41/numpy-2.2.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:41c5a21f4a04fa86436124d388f6ed60a9343a6f767fced1a8a71c3fbca038ff", size = 20875348 }, + { url = "https://files.pythonhosted.org/packages/66/ee/560deadcdde6c2f90200450d5938f63a34b37e27ebff162810f716f6a230/numpy-2.2.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de749064336d37e340f640b05f24e9e3dd678c57318c7289d222a8a2f543e90c", size = 14119362 }, + { url = "https://files.pythonhosted.org/packages/3c/65/4baa99f1c53b30adf0acd9a5519078871ddde8d2339dc5a7fde80d9d87da/numpy-2.2.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:894b3a42502226a1cac872f840030665f33326fc3dac8e57c607905773cdcde3", size = 5084103 }, + { url = "https://files.pythonhosted.org/packages/cc/89/e5a34c071a0570cc40c9a54eb472d113eea6d002e9ae12bb3a8407fb912e/numpy-2.2.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:71594f7c51a18e728451bb50cc60a3ce4e6538822731b2933209a1f3614e9282", size = 6625382 }, + { url = "https://files.pythonhosted.org/packages/f8/35/8c80729f1ff76b3921d5c9487c7ac3de9b2a103b1cd05e905b3090513510/numpy-2.2.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2618db89be1b4e05f7a1a847a9c1c0abd63e63a1607d892dd54668dd92faf87", size = 14018462 }, + { url = "https://files.pythonhosted.org/packages/8c/3d/1e1db36cfd41f895d266b103df00ca5b3cbe965184df824dec5c08c6b803/numpy-2.2.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd83c01228a688733f1ded5201c678f0c53ecc1006ffbc404db9f7a899ac6249", size = 16527618 }, + { url = "https://files.pythonhosted.org/packages/61/c6/03ed30992602c85aa3cd95b9070a514f8b3c33e31124694438d88809ae36/numpy-2.2.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:37c0ca431f82cd5fa716eca9506aefcabc247fb27ba69c5062a6d3ade8cf8f49", size = 15505511 }, + { url = "https://files.pythonhosted.org/packages/b7/25/5761d832a81df431e260719ec45de696414266613c9ee268394dd5ad8236/numpy-2.2.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fe27749d33bb772c80dcd84ae7e8df2adc920ae8297400dabec45f0dedb3f6de", size = 18313783 }, + { url = "https://files.pythonhosted.org/packages/57/0a/72d5a3527c5ebffcd47bde9162c39fae1f90138c961e5296491ce778e682/numpy-2.2.6-cp312-cp312-win32.whl", hash = "sha256:4eeaae00d789f66c7a25ac5f34b71a7035bb474e679f410e5e1a94deb24cf2d4", size = 6246506 }, + { url = "https://files.pythonhosted.org/packages/36/fa/8c9210162ca1b88529ab76b41ba02d433fd54fecaf6feb70ef9f124683f1/numpy-2.2.6-cp312-cp312-win_amd64.whl", hash = "sha256:c1f9540be57940698ed329904db803cf7a402f3fc200bfe599334c9bd84a40b2", size = 12614190 }, + { url = "https://files.pythonhosted.org/packages/f9/5c/6657823f4f594f72b5471f1db1ab12e26e890bb2e41897522d134d2a3e81/numpy-2.2.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0811bb762109d9708cca4d0b13c4f67146e3c3b7cf8d34018c722adb2d957c84", size = 20867828 }, + { url = "https://files.pythonhosted.org/packages/dc/9e/14520dc3dadf3c803473bd07e9b2bd1b69bc583cb2497b47000fed2fa92f/numpy-2.2.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:287cc3162b6f01463ccd86be154f284d0893d2b3ed7292439ea97eafa8170e0b", size = 14143006 }, + { url = "https://files.pythonhosted.org/packages/4f/06/7e96c57d90bebdce9918412087fc22ca9851cceaf5567a45c1f404480e9e/numpy-2.2.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:f1372f041402e37e5e633e586f62aa53de2eac8d98cbfb822806ce4bbefcb74d", size = 5076765 }, + { url = "https://files.pythonhosted.org/packages/73/ed/63d920c23b4289fdac96ddbdd6132e9427790977d5457cd132f18e76eae0/numpy-2.2.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:55a4d33fa519660d69614a9fad433be87e5252f4b03850642f88993f7b2ca566", size = 6617736 }, + { url = "https://files.pythonhosted.org/packages/85/c5/e19c8f99d83fd377ec8c7e0cf627a8049746da54afc24ef0a0cb73d5dfb5/numpy-2.2.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f92729c95468a2f4f15e9bb94c432a9229d0d50de67304399627a943201baa2f", size = 14010719 }, + { url = "https://files.pythonhosted.org/packages/19/49/4df9123aafa7b539317bf6d342cb6d227e49f7a35b99c287a6109b13dd93/numpy-2.2.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bc23a79bfabc5d056d106f9befb8d50c31ced2fbc70eedb8155aec74a45798f", size = 16526072 }, + { url = "https://files.pythonhosted.org/packages/b2/6c/04b5f47f4f32f7c2b0e7260442a8cbcf8168b0e1a41ff1495da42f42a14f/numpy-2.2.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e3143e4451880bed956e706a3220b4e5cf6172ef05fcc397f6f36a550b1dd868", size = 15503213 }, + { url = "https://files.pythonhosted.org/packages/17/0a/5cd92e352c1307640d5b6fec1b2ffb06cd0dabe7d7b8227f97933d378422/numpy-2.2.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4f13750ce79751586ae2eb824ba7e1e8dba64784086c98cdbbcc6a42112ce0d", size = 18316632 }, + { url = "https://files.pythonhosted.org/packages/f0/3b/5cba2b1d88760ef86596ad0f3d484b1cbff7c115ae2429678465057c5155/numpy-2.2.6-cp313-cp313-win32.whl", hash = "sha256:5beb72339d9d4fa36522fc63802f469b13cdbe4fdab4a288f0c441b74272ebfd", size = 6244532 }, + { url = "https://files.pythonhosted.org/packages/cb/3b/d58c12eafcb298d4e6d0d40216866ab15f59e55d148a5658bb3132311fcf/numpy-2.2.6-cp313-cp313-win_amd64.whl", hash = "sha256:b0544343a702fa80c95ad5d3d608ea3599dd54d4632df855e4c8d24eb6ecfa1c", size = 12610885 }, + { url = "https://files.pythonhosted.org/packages/6b/9e/4bf918b818e516322db999ac25d00c75788ddfd2d2ade4fa66f1f38097e1/numpy-2.2.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0bca768cd85ae743b2affdc762d617eddf3bcf8724435498a1e80132d04879e6", size = 20963467 }, + { url = "https://files.pythonhosted.org/packages/61/66/d2de6b291507517ff2e438e13ff7b1e2cdbdb7cb40b3ed475377aece69f9/numpy-2.2.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fc0c5673685c508a142ca65209b4e79ed6740a4ed6b2267dbba90f34b0b3cfda", size = 14225144 }, + { url = "https://files.pythonhosted.org/packages/e4/25/480387655407ead912e28ba3a820bc69af9adf13bcbe40b299d454ec011f/numpy-2.2.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:5bd4fc3ac8926b3819797a7c0e2631eb889b4118a9898c84f585a54d475b7e40", size = 5200217 }, + { url = "https://files.pythonhosted.org/packages/aa/4a/6e313b5108f53dcbf3aca0c0f3e9c92f4c10ce57a0a721851f9785872895/numpy-2.2.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:fee4236c876c4e8369388054d02d0e9bb84821feb1a64dd59e137e6511a551f8", size = 6712014 }, + { url = "https://files.pythonhosted.org/packages/b7/30/172c2d5c4be71fdf476e9de553443cf8e25feddbe185e0bd88b096915bcc/numpy-2.2.6-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1dda9c7e08dc141e0247a5b8f49cf05984955246a327d4c48bda16821947b2f", size = 14077935 }, + { url = "https://files.pythonhosted.org/packages/12/fb/9e743f8d4e4d3c710902cf87af3512082ae3d43b945d5d16563f26ec251d/numpy-2.2.6-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f447e6acb680fd307f40d3da4852208af94afdfab89cf850986c3ca00562f4fa", size = 16600122 }, + { url = "https://files.pythonhosted.org/packages/12/75/ee20da0e58d3a66f204f38916757e01e33a9737d0b22373b3eb5a27358f9/numpy-2.2.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:389d771b1623ec92636b0786bc4ae56abafad4a4c513d36a55dce14bd9ce8571", size = 15586143 }, + { url = "https://files.pythonhosted.org/packages/76/95/bef5b37f29fc5e739947e9ce5179ad402875633308504a52d188302319c8/numpy-2.2.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8e9ace4a37db23421249ed236fdcdd457d671e25146786dfc96835cd951aa7c1", size = 18385260 }, + { url = "https://files.pythonhosted.org/packages/09/04/f2f83279d287407cf36a7a8053a5abe7be3622a4363337338f2585e4afda/numpy-2.2.6-cp313-cp313t-win32.whl", hash = "sha256:038613e9fb8c72b0a41f025a7e4c3f0b7a1b5d768ece4796b674c8f3fe13efff", size = 6377225 }, + { url = "https://files.pythonhosted.org/packages/67/0e/35082d13c09c02c011cf21570543d202ad929d961c02a147493cb0c2bdf5/numpy-2.2.6-cp313-cp313t-win_amd64.whl", hash = "sha256:6031dd6dfecc0cf9f668681a37648373bddd6421fff6c66ec1624eed0180ee06", size = 12771374 }, +] + +[[package]] +name = "nvidia-cublas-cu12" +version = "12.4.5.8" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/7f/7fbae15a3982dc9595e49ce0f19332423b260045d0a6afe93cdbe2f1f624/nvidia_cublas_cu12-12.4.5.8-py3-none-manylinux2014_aarch64.whl", hash = "sha256:0f8aa1706812e00b9f19dfe0cdb3999b092ccb8ca168c0db5b8ea712456fd9b3", size = 363333771 }, + { url = "https://files.pythonhosted.org/packages/ae/71/1c91302526c45ab494c23f61c7a84aa568b8c1f9d196efa5993957faf906/nvidia_cublas_cu12-12.4.5.8-py3-none-manylinux2014_x86_64.whl", hash = "sha256:2fc8da60df463fdefa81e323eef2e36489e1c94335b5358bcb38360adf75ac9b", size = 363438805 }, +] + +[[package]] +name = "nvidia-cuda-cupti-cu12" +version = "12.4.127" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/93/b5/9fb3d00386d3361b03874246190dfec7b206fd74e6e287b26a8fcb359d95/nvidia_cuda_cupti_cu12-12.4.127-py3-none-manylinux2014_aarch64.whl", hash = "sha256:79279b35cf6f91da114182a5ce1864997fd52294a87a16179ce275773799458a", size = 12354556 }, + { url = "https://files.pythonhosted.org/packages/67/42/f4f60238e8194a3106d06a058d494b18e006c10bb2b915655bd9f6ea4cb1/nvidia_cuda_cupti_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:9dec60f5ac126f7bb551c055072b69d85392b13311fcc1bcda2202d172df30fb", size = 13813957 }, +] + +[[package]] +name = "nvidia-cuda-nvrtc-cu12" +version = "12.4.127" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/77/aa/083b01c427e963ad0b314040565ea396f914349914c298556484f799e61b/nvidia_cuda_nvrtc_cu12-12.4.127-py3-none-manylinux2014_aarch64.whl", hash = "sha256:0eedf14185e04b76aa05b1fea04133e59f465b6f960c0cbf4e37c3cb6b0ea198", size = 24133372 }, + { url = "https://files.pythonhosted.org/packages/2c/14/91ae57cd4db3f9ef7aa99f4019cfa8d54cb4caa7e00975df6467e9725a9f/nvidia_cuda_nvrtc_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:a178759ebb095827bd30ef56598ec182b85547f1508941a3d560eb7ea1fbf338", size = 24640306 }, +] + +[[package]] +name = "nvidia-cuda-runtime-cu12" +version = "12.4.127" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a1/aa/b656d755f474e2084971e9a297def515938d56b466ab39624012070cb773/nvidia_cuda_runtime_cu12-12.4.127-py3-none-manylinux2014_aarch64.whl", hash = "sha256:961fe0e2e716a2a1d967aab7caee97512f71767f852f67432d572e36cb3a11f3", size = 894177 }, + { url = "https://files.pythonhosted.org/packages/ea/27/1795d86fe88ef397885f2e580ac37628ed058a92ed2c39dc8eac3adf0619/nvidia_cuda_runtime_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:64403288fa2136ee8e467cdc9c9427e0434110899d07c779f25b5c068934faa5", size = 883737 }, +] + +[[package]] +name = "nvidia-cudnn-cu12" +version = "9.1.0.70" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-cublas-cu12", marker = "platform_machine != 'aarch64' and sys_platform == 'linux'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/9f/fd/713452cd72343f682b1c7b9321e23829f00b842ceaedcda96e742ea0b0b3/nvidia_cudnn_cu12-9.1.0.70-py3-none-manylinux2014_x86_64.whl", hash = "sha256:165764f44ef8c61fcdfdfdbe769d687e06374059fbb388b6c89ecb0e28793a6f", size = 664752741 }, +] + +[[package]] +name = "nvidia-cufft-cu12" +version = "11.2.1.3" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7a/8a/0e728f749baca3fbeffad762738276e5df60851958be7783af121a7221e7/nvidia_cufft_cu12-11.2.1.3-py3-none-manylinux2014_aarch64.whl", hash = "sha256:5dad8008fc7f92f5ddfa2101430917ce2ffacd86824914c82e28990ad7f00399", size = 211422548 }, + { url = "https://files.pythonhosted.org/packages/27/94/3266821f65b92b3138631e9c8e7fe1fb513804ac934485a8d05776e1dd43/nvidia_cufft_cu12-11.2.1.3-py3-none-manylinux2014_x86_64.whl", hash = "sha256:f083fc24912aa410be21fa16d157fed2055dab1cc4b6934a0e03cba69eb242b9", size = 211459117 }, +] + +[[package]] +name = "nvidia-curand-cu12" +version = "10.3.5.147" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/80/9c/a79180e4d70995fdf030c6946991d0171555c6edf95c265c6b2bf7011112/nvidia_curand_cu12-10.3.5.147-py3-none-manylinux2014_aarch64.whl", hash = "sha256:1f173f09e3e3c76ab084aba0de819c49e56614feae5c12f69883f4ae9bb5fad9", size = 56314811 }, + { url = "https://files.pythonhosted.org/packages/8a/6d/44ad094874c6f1b9c654f8ed939590bdc408349f137f9b98a3a23ccec411/nvidia_curand_cu12-10.3.5.147-py3-none-manylinux2014_x86_64.whl", hash = "sha256:a88f583d4e0bb643c49743469964103aa59f7f708d862c3ddb0fc07f851e3b8b", size = 56305206 }, +] + +[[package]] +name = "nvidia-cusolver-cu12" +version = "11.6.1.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-cublas-cu12", marker = "platform_machine != 'aarch64' and sys_platform == 'linux'" }, + { name = "nvidia-cusparse-cu12", marker = "platform_machine != 'aarch64' and sys_platform == 'linux'" }, + { name = "nvidia-nvjitlink-cu12", marker = "platform_machine != 'aarch64' and sys_platform == 'linux'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/46/6b/a5c33cf16af09166845345275c34ad2190944bcc6026797a39f8e0a282e0/nvidia_cusolver_cu12-11.6.1.9-py3-none-manylinux2014_aarch64.whl", hash = "sha256:d338f155f174f90724bbde3758b7ac375a70ce8e706d70b018dd3375545fc84e", size = 127634111 }, + { url = "https://files.pythonhosted.org/packages/3a/e1/5b9089a4b2a4790dfdea8b3a006052cfecff58139d5a4e34cb1a51df8d6f/nvidia_cusolver_cu12-11.6.1.9-py3-none-manylinux2014_x86_64.whl", hash = "sha256:19e33fa442bcfd085b3086c4ebf7e8debc07cfe01e11513cc6d332fd918ac260", size = 127936057 }, +] + +[[package]] +name = "nvidia-cusparse-cu12" +version = "12.3.1.170" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-nvjitlink-cu12", marker = "platform_machine != 'aarch64' and sys_platform == 'linux'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/96/a9/c0d2f83a53d40a4a41be14cea6a0bf9e668ffcf8b004bd65633f433050c0/nvidia_cusparse_cu12-12.3.1.170-py3-none-manylinux2014_aarch64.whl", hash = "sha256:9d32f62896231ebe0480efd8a7f702e143c98cfaa0e8a76df3386c1ba2b54df3", size = 207381987 }, + { url = "https://files.pythonhosted.org/packages/db/f7/97a9ea26ed4bbbfc2d470994b8b4f338ef663be97b8f677519ac195e113d/nvidia_cusparse_cu12-12.3.1.170-py3-none-manylinux2014_x86_64.whl", hash = "sha256:ea4f11a2904e2a8dc4b1833cc1b5181cde564edd0d5cd33e3c168eff2d1863f1", size = 207454763 }, +] + +[[package]] +name = "nvidia-cusparselt-cu12" +version = "0.6.2" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/98/8e/675498726c605c9441cf46653bd29cb1b8666da1fb1469ffa25f67f20c58/nvidia_cusparselt_cu12-0.6.2-py3-none-manylinux2014_aarch64.whl", hash = "sha256:067a7f6d03ea0d4841c85f0c6f1991c5dda98211f6302cb83a4ab234ee95bef8", size = 149422781 }, + { url = "https://files.pythonhosted.org/packages/78/a8/bcbb63b53a4b1234feeafb65544ee55495e1bb37ec31b999b963cbccfd1d/nvidia_cusparselt_cu12-0.6.2-py3-none-manylinux2014_x86_64.whl", hash = "sha256:df2c24502fd76ebafe7457dbc4716b2fec071aabaed4fb7691a201cde03704d9", size = 150057751 }, +] + +[[package]] +name = "nvidia-nccl-cu12" +version = "2.21.5" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/99/12cd266d6233f47d00daf3a72739872bdc10267d0383508b0b9c84a18bb6/nvidia_nccl_cu12-2.21.5-py3-none-manylinux2014_x86_64.whl", hash = "sha256:8579076d30a8c24988834445f8d633c697d42397e92ffc3f63fa26766d25e0a0", size = 188654414 }, +] + +[[package]] +name = "nvidia-nvjitlink-cu12" +version = "12.4.127" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/02/45/239d52c05074898a80a900f49b1615d81c07fceadd5ad6c4f86a987c0bc4/nvidia_nvjitlink_cu12-12.4.127-py3-none-manylinux2014_aarch64.whl", hash = "sha256:4abe7fef64914ccfa909bc2ba39739670ecc9e820c83ccc7a6ed414122599b83", size = 20552510 }, + { url = "https://files.pythonhosted.org/packages/ff/ff/847841bacfbefc97a00036e0fce5a0f086b640756dc38caea5e1bb002655/nvidia_nvjitlink_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:06b3b9b25bf3f8af351d664978ca26a16d2c5127dbd53c0497e28d1fb9611d57", size = 21066810 }, +] + +[[package]] +name = "nvidia-nvtx-cu12" +version = "12.4.127" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/06/39/471f581edbb7804b39e8063d92fc8305bdc7a80ae5c07dbe6ea5c50d14a5/nvidia_nvtx_cu12-12.4.127-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7959ad635db13edf4fc65c06a6e9f9e55fc2f92596db928d169c0bb031e88ef3", size = 100417 }, + { url = "https://files.pythonhosted.org/packages/87/20/199b8713428322a2f22b722c62b8cc278cc53dffa9705d744484b5035ee9/nvidia_nvtx_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:781e950d9b9f60d8241ccea575b32f5105a5baf4c2351cab5256a24869f12a1a", size = 99144 }, +] + +[[package]] +name = "opencv-python" +version = "4.11.0.86" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/17/06/68c27a523103dad5837dc5b87e71285280c4f098c60e4fe8a8db6486ab09/opencv-python-4.11.0.86.tar.gz", hash = "sha256:03d60ccae62304860d232272e4a4fda93c39d595780cb40b161b310244b736a4", size = 95171956 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/05/4d/53b30a2a3ac1f75f65a59eb29cf2ee7207ce64867db47036ad61743d5a23/opencv_python-4.11.0.86-cp37-abi3-macosx_13_0_arm64.whl", hash = "sha256:432f67c223f1dc2824f5e73cdfcd9db0efc8710647d4e813012195dc9122a52a", size = 37326322 }, + { url = "https://files.pythonhosted.org/packages/3b/84/0a67490741867eacdfa37bc18df96e08a9d579583b419010d7f3da8ff503/opencv_python-4.11.0.86-cp37-abi3-macosx_13_0_x86_64.whl", hash = "sha256:9d05ef13d23fe97f575153558653e2d6e87103995d54e6a35db3f282fe1f9c66", size = 56723197 }, + { url = "https://files.pythonhosted.org/packages/f3/bd/29c126788da65c1fb2b5fb621b7fed0ed5f9122aa22a0868c5e2c15c6d23/opencv_python-4.11.0.86-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b92ae2c8852208817e6776ba1ea0d6b1e0a1b5431e971a2a0ddd2a8cc398202", size = 42230439 }, + { url = "https://files.pythonhosted.org/packages/2c/8b/90eb44a40476fa0e71e05a0283947cfd74a5d36121a11d926ad6f3193cc4/opencv_python-4.11.0.86-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b02611523803495003bd87362db3e1d2a0454a6a63025dc6658a9830570aa0d", size = 62986597 }, + { url = "https://files.pythonhosted.org/packages/fb/d7/1d5941a9dde095468b288d989ff6539dd69cd429dbf1b9e839013d21b6f0/opencv_python-4.11.0.86-cp37-abi3-win32.whl", hash = "sha256:810549cb2a4aedaa84ad9a1c92fbfdfc14090e2749cedf2c1589ad8359aa169b", size = 29384337 }, + { url = "https://files.pythonhosted.org/packages/a4/7d/f1c30a92854540bf789e9cd5dde7ef49bbe63f855b85a2e6b3db8135c591/opencv_python-4.11.0.86-cp37-abi3-win_amd64.whl", hash = "sha256:085ad9b77c18853ea66283e98affefe2de8cc4c1f43eda4c100cf9b2721142ec", size = 39488044 }, +] + +[[package]] +name = "openpyxl" +version = "3.1.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "et-xmlfile" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3d/f9/88d94a75de065ea32619465d2f77b29a0469500e99012523b91cc4141cd1/openpyxl-3.1.5.tar.gz", hash = "sha256:cf0e3cf56142039133628b5acffe8ef0c12bc902d2aadd3e0fe5878dc08d1050", size = 186464 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c0/da/977ded879c29cbd04de313843e76868e6e13408a94ed6b987245dc7c8506/openpyxl-3.1.5-py2.py3-none-any.whl", hash = "sha256:5282c12b107bffeef825f4617dc029afaf41d0ea60823bbb665ef3079dc79de2", size = 250910 }, +] + +[[package]] +name = "openslide-bin" +version = "4.0.0.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/61/49/0a62d150b620bf045ddf1e70ae90e42e423aefdc07b4927d846faefcca19/openslide-bin-4.0.0.8.tar.gz", hash = "sha256:bae3b5e374ada9d6b5110d19f60a71e1d164578be98c46c0761c028dec8949e4", size = 17872279 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f9/4b/d527b317dddd9fe17addf16a49eed0907943590228ccf2d2f535e541e928/openslide_bin-4.0.0.8-py3-none-macosx_11_0_universal2.whl", hash = "sha256:9389cf7bb1b4ecd9e79e6d00151ae6a43a1e8b07ce7119609324d9260345f3c7", size = 5372459 }, + { url = "https://files.pythonhosted.org/packages/8c/be/f4028886d0e2bc4f4114bfa929427ec29a6be86831327f131d423fde041c/openslide_bin-4.0.0.8-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:79c94c39a48d497caa39ecdad5ef64a931b31c6592354a142497bc3db2e88494", size = 4221147 }, + { url = "https://files.pythonhosted.org/packages/d7/a0/7255bfd87c47b18f29fe78719b1e9fd075624c8d7cb957d65b951468cc38/openslide_bin-4.0.0.8-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:d9cb149af1a5c2eddbc0e3b75f552d932a840689f011d53724912de37071cc40", size = 4327171 }, + { url = "https://files.pythonhosted.org/packages/b1/05/c01a0c145ba88b19acd9874693774e6bedb7aa1bb1fa4c0b40e1bc42851e/openslide_bin-4.0.0.8-py3-none-win_amd64.whl", hash = "sha256:a06abf5b6f7807fa7aad2b240496a1d825cbd885267d521458eacbcfd782ed44", size = 4176324 }, +] + +[[package]] +name = "openslide-python" +version = "1.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pillow" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6e/c7/1b79b6f31aa23d33ae93d7fd34fdf0d03569dbfa35a0689be8ce27471c05/openslide_python-1.4.2.tar.gz", hash = "sha256:610d7a71552afe7be33038c10d8a2b20b9824ca2836710870e81f193257411bc", size = 386361 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/02/12/b6e8ec85746f02aa5433cf2eaf2493ef8cdf5ef0a8675278a0263db6d88e/openslide_python-1.4.2-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:0f459f0e071048a5e3cafbf814fdac92da55df9b07730a832b54b99191f8a612", size = 33187 }, + { url = "https://files.pythonhosted.org/packages/c5/2f/d3004b89dc7810db756c367cf6c95040864f79323dc25e8f9bbd0cf71c94/openslide_python-1.4.2-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b24566c2167d5909b4f54d5ee53c1caada5ec1aa14921a19de67a694ed988765", size = 37375 }, + { url = "https://files.pythonhosted.org/packages/82/75/664b94930660f124a36d15afd8e7c4b56d8c9b3e36c0cbe76311aaaa65b0/openslide_python-1.4.2-cp311-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:69cc23d608120bc528399d006c47501ac5e390770d4d5090800defd51be1b595", size = 36794 }, + { url = "https://files.pythonhosted.org/packages/7f/5d/5e8825f93cfa305fb2e601b947dd55741bad49fddf12e43bf3b8f07100aa/openslide_python-1.4.2-cp311-abi3-win_amd64.whl", hash = "sha256:e53cfb967c0987c76a9b890bb9551e238b12a99d947362434a08d4aa40ba1739", size = 35385 }, +] + +[[package]] +name = "osqp" +version = "0.6.7.post3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, + { name = "qdldl" }, + { name = "scipy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/74/35/45d4d1832b31d207f83e0f9734d041be125fb4f0dff49413674bd1b08032/osqp-0.6.7.post3.tar.gz", hash = "sha256:b0c5e0a721f21c9724097a4fd50108304d296468d124e16f34ac67046f7020e1", size = 229274 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f2/dd/123079f0ad8409d3be9074344a3d45073ce928f701890f010ab506ffee9f/osqp-0.6.7.post3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b1a1dcd869fd6ac501e06262c21483a3691b6281e4f3f65af6951330958b89ca", size = 251844 }, + { url = "https://files.pythonhosted.org/packages/cd/6d/0d17e8fa61809c125f97685d86e6cd6f7b1e745e01b8d3f96d783c8de41b/osqp-0.6.7.post3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:46b93d1110dc0ad311f6691c4df9ee41cbbde5ffc0d8c8d520d4555bf5d8765b", size = 237567 }, + { url = "https://files.pythonhosted.org/packages/4f/74/d748a9f42426fa48ab0139d0738988296a3c599a6b3c78395258d02e436b/osqp-0.6.7.post3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5209104d6fe3ace4fdbf9ace08caa2cba9de1e7ccd5f56279a346c235917138b", size = 293855 }, + { url = "https://files.pythonhosted.org/packages/55/72/8746c4bc488a31641091ccc50e71f92e0a4211e2ef882e00904940531962/osqp-0.6.7.post3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdfefa07740e9fb1c574cdc836e5afe2600b73c0c12089955d4ae6587c55f0eb", size = 298312 }, + { url = "https://files.pythonhosted.org/packages/f8/7b/ec42030f389c1b2a7e5517d4ba4a169f1d8fb6f4beb92c5b457e0cc284e4/osqp-0.6.7.post3-cp311-cp311-win_amd64.whl", hash = "sha256:c48c91dfba02ce11e8b8f5d401ec5b67a316782bfdf4f53ca753e49907f7387f", size = 293043 }, + { url = "https://files.pythonhosted.org/packages/22/26/4cf65e82cf63c4f4ff5186618c006d95a1a5bc9f4f015563ad6d87d75a42/osqp-0.6.7.post3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:023af06764f7aba9c64536ecb7204019906bb7e78237f335f82b404f16623eef", size = 252062 }, + { url = "https://files.pythonhosted.org/packages/ce/bc/ece5348baef40bf355c5ef8000103aaf77973060f4c940da9cce0999e00d/osqp-0.6.7.post3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4cec7cb5bf1615c4129277275dc08e20a037372a874cff35eb891b4b35a463de", size = 237577 }, + { url = "https://files.pythonhosted.org/packages/31/33/f09c305591606e59edc5f09aa5cba3606c0e29e7b0fff42d044585bcc1f4/osqp-0.6.7.post3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eb882ab24b97b14843b7c71d2474fb8b415bafc8dd60aa94870c2ef338c20bfb", size = 295407 }, + { url = "https://files.pythonhosted.org/packages/ef/63/356f01888eb0e4cd8603eb8b7711a6865e26bc2d9a1882a1e4562333debd/osqp-0.6.7.post3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:502fde0ae710cef1e6418fb8d26efef9597d1dcba877489a1c2eb9c3eb2ff2e9", size = 300002 }, + { url = "https://files.pythonhosted.org/packages/57/b5/958d4188cb9347e420d3de2d19d8cb1113f691b7a093cdef67f86b598f30/osqp-0.6.7.post3-cp312-cp312-win_amd64.whl", hash = "sha256:468588cfb690becba4d1f460c2a53e75530584e3efcf2caed59f5219032e6888", size = 293164 }, + { url = "https://files.pythonhosted.org/packages/94/78/f01a209777678e94546b8c43c12a28db16094dfeea689e9db6d59d3b89ad/osqp-0.6.7.post3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:cee478eedf9cfad11ff9c27ef0b1e032506a16888b8b874f622816cf8749db7f", size = 251946 }, + { url = "https://files.pythonhosted.org/packages/05/89/2d2dc40ebe25f92901d52d706bf8f31ea5718570e55828f1000fd380911f/osqp-0.6.7.post3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5dd739c4c6c91e40d2e3ea2bb78c635c897e07697ab24a46d3a5d197e254b0f3", size = 237548 }, + { url = "https://files.pythonhosted.org/packages/65/d3/76c076599a290f6ed5b7ad3bd2c68fa0dd198ccd63fec17f004b859843e4/osqp-0.6.7.post3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:002f280f23d15ad3c6386a868688f0b17c90dba13d0f7f8da1c833a14fc4d7f8", size = 295476 }, + { url = "https://files.pythonhosted.org/packages/63/29/b8c9cf93a6a04399960e51c92c92a195d32b80a330c6a25a51d300b86e1a/osqp-0.6.7.post3-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a2922fe8cb666964cf01b643da81eadf4bb435139a5f042d5bb6dcb87496778", size = 300072 }, + { url = "https://files.pythonhosted.org/packages/4b/0a/acd48ad432ccf2538972805095108801a3b29a2433b48bd3a34e640df1e4/osqp-0.6.7.post3-cp313-cp313-win_amd64.whl", hash = "sha256:acb219e941f5248da5de3ee9b70e6a5aaddf5f3989dffd1d4c03b0f7b1dfa17b", size = 293170 }, +] + +[[package]] +name = "packaging" +version = "24.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 }, +] + +[[package]] +name = "pandas" +version = "2.2.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, + { name = "python-dateutil" }, + { name = "pytz" }, + { name = "tzdata" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9c/d6/9f8431bacc2e19dca897724cd097b1bb224a6ad5433784a44b587c7c13af/pandas-2.2.3.tar.gz", hash = "sha256:4f18ba62b61d7e192368b84517265a99b4d7ee8912f8708660fb4a366cc82667", size = 4399213 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a8/44/d9502bf0ed197ba9bf1103c9867d5904ddcaf869e52329787fc54ed70cc8/pandas-2.2.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:66108071e1b935240e74525006034333f98bcdb87ea116de573a6a0dccb6c039", size = 12602222 }, + { url = "https://files.pythonhosted.org/packages/52/11/9eac327a38834f162b8250aab32a6781339c69afe7574368fffe46387edf/pandas-2.2.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7c2875855b0ff77b2a64a0365e24455d9990730d6431b9e0ee18ad8acee13dbd", size = 11321274 }, + { url = "https://files.pythonhosted.org/packages/45/fb/c4beeb084718598ba19aa9f5abbc8aed8b42f90930da861fcb1acdb54c3a/pandas-2.2.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cd8d0c3be0515c12fed0bdbae072551c8b54b7192c7b1fda0ba56059a0179698", size = 15579836 }, + { url = "https://files.pythonhosted.org/packages/cd/5f/4dba1d39bb9c38d574a9a22548c540177f78ea47b32f99c0ff2ec499fac5/pandas-2.2.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c124333816c3a9b03fbeef3a9f230ba9a737e9e5bb4060aa2107a86cc0a497fc", size = 13058505 }, + { url = "https://files.pythonhosted.org/packages/b9/57/708135b90391995361636634df1f1130d03ba456e95bcf576fada459115a/pandas-2.2.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:63cc132e40a2e084cf01adf0775b15ac515ba905d7dcca47e9a251819c575ef3", size = 16744420 }, + { url = "https://files.pythonhosted.org/packages/86/4a/03ed6b7ee323cf30404265c284cee9c65c56a212e0a08d9ee06984ba2240/pandas-2.2.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:29401dbfa9ad77319367d36940cd8a0b3a11aba16063e39632d98b0e931ddf32", size = 14440457 }, + { url = "https://files.pythonhosted.org/packages/ed/8c/87ddf1fcb55d11f9f847e3c69bb1c6f8e46e2f40ab1a2d2abadb2401b007/pandas-2.2.3-cp311-cp311-win_amd64.whl", hash = "sha256:3fc6873a41186404dad67245896a6e440baacc92f5b716ccd1bc9ed2995ab2c5", size = 11617166 }, + { url = "https://files.pythonhosted.org/packages/17/a3/fb2734118db0af37ea7433f57f722c0a56687e14b14690edff0cdb4b7e58/pandas-2.2.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b1d432e8d08679a40e2a6d8b2f9770a5c21793a6f9f47fdd52c5ce1948a5a8a9", size = 12529893 }, + { url = "https://files.pythonhosted.org/packages/e1/0c/ad295fd74bfac85358fd579e271cded3ac969de81f62dd0142c426b9da91/pandas-2.2.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a5a1595fe639f5988ba6a8e5bc9649af3baf26df3998a0abe56c02609392e0a4", size = 11363475 }, + { url = "https://files.pythonhosted.org/packages/c6/2a/4bba3f03f7d07207481fed47f5b35f556c7441acddc368ec43d6643c5777/pandas-2.2.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5de54125a92bb4d1c051c0659e6fcb75256bf799a732a87184e5ea503965bce3", size = 15188645 }, + { url = "https://files.pythonhosted.org/packages/38/f8/d8fddee9ed0d0c0f4a2132c1dfcf0e3e53265055da8df952a53e7eaf178c/pandas-2.2.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fffb8ae78d8af97f849404f21411c95062db1496aeb3e56f146f0355c9989319", size = 12739445 }, + { url = "https://files.pythonhosted.org/packages/20/e8/45a05d9c39d2cea61ab175dbe6a2de1d05b679e8de2011da4ee190d7e748/pandas-2.2.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6dfcb5ee8d4d50c06a51c2fffa6cff6272098ad6540aed1a76d15fb9318194d8", size = 16359235 }, + { url = "https://files.pythonhosted.org/packages/1d/99/617d07a6a5e429ff90c90da64d428516605a1ec7d7bea494235e1c3882de/pandas-2.2.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:062309c1b9ea12a50e8ce661145c6aab431b1e99530d3cd60640e255778bd43a", size = 14056756 }, + { url = "https://files.pythonhosted.org/packages/29/d4/1244ab8edf173a10fd601f7e13b9566c1b525c4f365d6bee918e68381889/pandas-2.2.3-cp312-cp312-win_amd64.whl", hash = "sha256:59ef3764d0fe818125a5097d2ae867ca3fa64df032331b7e0917cf5d7bf66b13", size = 11504248 }, + { url = "https://files.pythonhosted.org/packages/64/22/3b8f4e0ed70644e85cfdcd57454686b9057c6c38d2f74fe4b8bc2527214a/pandas-2.2.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f00d1345d84d8c86a63e476bb4955e46458b304b9575dcf71102b5c705320015", size = 12477643 }, + { url = "https://files.pythonhosted.org/packages/e4/93/b3f5d1838500e22c8d793625da672f3eec046b1a99257666c94446969282/pandas-2.2.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3508d914817e153ad359d7e069d752cdd736a247c322d932eb89e6bc84217f28", size = 11281573 }, + { url = "https://files.pythonhosted.org/packages/f5/94/6c79b07f0e5aab1dcfa35a75f4817f5c4f677931d4234afcd75f0e6a66ca/pandas-2.2.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:22a9d949bfc9a502d320aa04e5d02feab689d61da4e7764b62c30b991c42c5f0", size = 15196085 }, + { url = "https://files.pythonhosted.org/packages/e8/31/aa8da88ca0eadbabd0a639788a6da13bb2ff6edbbb9f29aa786450a30a91/pandas-2.2.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3a255b2c19987fbbe62a9dfd6cff7ff2aa9ccab3fc75218fd4b7530f01efa24", size = 12711809 }, + { url = "https://files.pythonhosted.org/packages/ee/7c/c6dbdb0cb2a4344cacfb8de1c5808ca885b2e4dcfde8008266608f9372af/pandas-2.2.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:800250ecdadb6d9c78eae4990da62743b857b470883fa27f652db8bdde7f6659", size = 16356316 }, + { url = "https://files.pythonhosted.org/packages/57/b7/8b757e7d92023b832869fa8881a992696a0bfe2e26f72c9ae9f255988d42/pandas-2.2.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6374c452ff3ec675a8f46fd9ab25c4ad0ba590b71cf0656f8b6daa5202bca3fb", size = 14022055 }, + { url = "https://files.pythonhosted.org/packages/3b/bc/4b18e2b8c002572c5a441a64826252ce5da2aa738855747247a971988043/pandas-2.2.3-cp313-cp313-win_amd64.whl", hash = "sha256:61c5ad4043f791b61dd4752191d9f07f0ae412515d59ba8f005832a532f8736d", size = 11481175 }, + { url = "https://files.pythonhosted.org/packages/76/a3/a5d88146815e972d40d19247b2c162e88213ef51c7c25993942c39dbf41d/pandas-2.2.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3b71f27954685ee685317063bf13c7709a7ba74fc996b84fc6821c59b0f06468", size = 12615650 }, + { url = "https://files.pythonhosted.org/packages/9c/8c/f0fd18f6140ddafc0c24122c8a964e48294acc579d47def376fef12bcb4a/pandas-2.2.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:38cf8125c40dae9d5acc10fa66af8ea6fdf760b2714ee482ca691fc66e6fcb18", size = 11290177 }, + { url = "https://files.pythonhosted.org/packages/ed/f9/e995754eab9c0f14c6777401f7eece0943840b7a9fc932221c19d1abee9f/pandas-2.2.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ba96630bc17c875161df3818780af30e43be9b166ce51c9a18c1feae342906c2", size = 14651526 }, + { url = "https://files.pythonhosted.org/packages/25/b0/98d6ae2e1abac4f35230aa756005e8654649d305df9a28b16b9ae4353bff/pandas-2.2.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1db71525a1538b30142094edb9adc10be3f3e176748cd7acc2240c2f2e5aa3a4", size = 11871013 }, + { url = "https://files.pythonhosted.org/packages/cc/57/0f72a10f9db6a4628744c8e8f0df4e6e21de01212c7c981d31e50ffc8328/pandas-2.2.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:15c0e1e02e93116177d29ff83e8b1619c93ddc9c49083f237d4312337a61165d", size = 15711620 }, + { url = "https://files.pythonhosted.org/packages/ab/5f/b38085618b950b79d2d9164a711c52b10aefc0ae6833b96f626b7021b2ed/pandas-2.2.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ad5b65698ab28ed8d7f18790a0dc58005c7629f227be9ecc1072aa74c0c1d43a", size = 13098436 }, +] + +[[package]] +name = "parso" +version = "0.8.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/66/94/68e2e17afaa9169cf6412ab0f28623903be73d1b32e208d9e8e541bb086d/parso-0.8.4.tar.gz", hash = "sha256:eb3a7b58240fb99099a345571deecc0f9540ea5f4dd2fe14c2a99d6b281ab92d", size = 400609 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c6/ac/dac4a63f978e4dcb3c6d3a78c4d8e0192a113d288502a1216950c41b1027/parso-0.8.4-py2.py3-none-any.whl", hash = "sha256:a418670a20291dacd2dddc80c377c5c3791378ee1e8d12bffc35420643d43f18", size = 103650 }, +] + +[[package]] +name = "pexpect" +version = "4.9.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "ptyprocess" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523", size = 63772 }, +] + +[[package]] +name = "pillow" +version = "11.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/af/cb/bb5c01fcd2a69335b86c22142b2bccfc3464087efb7fd382eee5ffc7fdf7/pillow-11.2.1.tar.gz", hash = "sha256:a64dd61998416367b7ef979b73d3a85853ba9bec4c2925f74e588879a58716b6", size = 47026707 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/68/08/3fbf4b98924c73037a8e8b4c2c774784805e0fb4ebca6c5bb60795c40125/pillow-11.2.1-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:35ca289f712ccfc699508c4658a1d14652e8033e9b69839edf83cbdd0ba39e70", size = 3198450 }, + { url = "https://files.pythonhosted.org/packages/84/92/6505b1af3d2849d5e714fc75ba9e69b7255c05ee42383a35a4d58f576b16/pillow-11.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e0409af9f829f87a2dfb7e259f78f317a5351f2045158be321fd135973fff7bf", size = 3030550 }, + { url = "https://files.pythonhosted.org/packages/3c/8c/ac2f99d2a70ff966bc7eb13dacacfaab57c0549b2ffb351b6537c7840b12/pillow-11.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4e5c5edee874dce4f653dbe59db7c73a600119fbea8d31f53423586ee2aafd7", size = 4415018 }, + { url = "https://files.pythonhosted.org/packages/1f/e3/0a58b5d838687f40891fff9cbaf8669f90c96b64dc8f91f87894413856c6/pillow-11.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b93a07e76d13bff9444f1a029e0af2964e654bfc2e2c2d46bfd080df5ad5f3d8", size = 4498006 }, + { url = "https://files.pythonhosted.org/packages/21/f5/6ba14718135f08fbfa33308efe027dd02b781d3f1d5c471444a395933aac/pillow-11.2.1-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:e6def7eed9e7fa90fde255afaf08060dc4b343bbe524a8f69bdd2a2f0018f600", size = 4517773 }, + { url = "https://files.pythonhosted.org/packages/20/f2/805ad600fc59ebe4f1ba6129cd3a75fb0da126975c8579b8f57abeb61e80/pillow-11.2.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:8f4f3724c068be008c08257207210c138d5f3731af6c155a81c2b09a9eb3a788", size = 4607069 }, + { url = "https://files.pythonhosted.org/packages/71/6b/4ef8a288b4bb2e0180cba13ca0a519fa27aa982875882392b65131401099/pillow-11.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a0a6709b47019dff32e678bc12c63008311b82b9327613f534e496dacaefb71e", size = 4583460 }, + { url = "https://files.pythonhosted.org/packages/62/ae/f29c705a09cbc9e2a456590816e5c234382ae5d32584f451c3eb41a62062/pillow-11.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f6b0c664ccb879109ee3ca702a9272d877f4fcd21e5eb63c26422fd6e415365e", size = 4661304 }, + { url = "https://files.pythonhosted.org/packages/6e/1a/c8217b6f2f73794a5e219fbad087701f412337ae6dbb956db37d69a9bc43/pillow-11.2.1-cp311-cp311-win32.whl", hash = "sha256:cc5d875d56e49f112b6def6813c4e3d3036d269c008bf8aef72cd08d20ca6df6", size = 2331809 }, + { url = "https://files.pythonhosted.org/packages/e2/72/25a8f40170dc262e86e90f37cb72cb3de5e307f75bf4b02535a61afcd519/pillow-11.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:0f5c7eda47bf8e3c8a283762cab94e496ba977a420868cb819159980b6709193", size = 2676338 }, + { url = "https://files.pythonhosted.org/packages/06/9e/76825e39efee61efea258b479391ca77d64dbd9e5804e4ad0fa453b4ba55/pillow-11.2.1-cp311-cp311-win_arm64.whl", hash = "sha256:4d375eb838755f2528ac8cbc926c3e31cc49ca4ad0cf79cff48b20e30634a4a7", size = 2414918 }, + { url = "https://files.pythonhosted.org/packages/c7/40/052610b15a1b8961f52537cc8326ca6a881408bc2bdad0d852edeb6ed33b/pillow-11.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:78afba22027b4accef10dbd5eed84425930ba41b3ea0a86fa8d20baaf19d807f", size = 3190185 }, + { url = "https://files.pythonhosted.org/packages/e5/7e/b86dbd35a5f938632093dc40d1682874c33dcfe832558fc80ca56bfcb774/pillow-11.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:78092232a4ab376a35d68c4e6d5e00dfd73454bd12b230420025fbe178ee3b0b", size = 3030306 }, + { url = "https://files.pythonhosted.org/packages/a4/5c/467a161f9ed53e5eab51a42923c33051bf8d1a2af4626ac04f5166e58e0c/pillow-11.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25a5f306095c6780c52e6bbb6109624b95c5b18e40aab1c3041da3e9e0cd3e2d", size = 4416121 }, + { url = "https://files.pythonhosted.org/packages/62/73/972b7742e38ae0e2ac76ab137ca6005dcf877480da0d9d61d93b613065b4/pillow-11.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c7b29dbd4281923a2bfe562acb734cee96bbb129e96e6972d315ed9f232bef4", size = 4501707 }, + { url = "https://files.pythonhosted.org/packages/e4/3a/427e4cb0b9e177efbc1a84798ed20498c4f233abde003c06d2650a6d60cb/pillow-11.2.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:3e645b020f3209a0181a418bffe7b4a93171eef6c4ef6cc20980b30bebf17b7d", size = 4522921 }, + { url = "https://files.pythonhosted.org/packages/fe/7c/d8b1330458e4d2f3f45d9508796d7caf0c0d3764c00c823d10f6f1a3b76d/pillow-11.2.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:b2dbea1012ccb784a65349f57bbc93730b96e85b42e9bf7b01ef40443db720b4", size = 4612523 }, + { url = "https://files.pythonhosted.org/packages/b3/2f/65738384e0b1acf451de5a573d8153fe84103772d139e1e0bdf1596be2ea/pillow-11.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:da3104c57bbd72948d75f6a9389e6727d2ab6333c3617f0a89d72d4940aa0443", size = 4587836 }, + { url = "https://files.pythonhosted.org/packages/6a/c5/e795c9f2ddf3debb2dedd0df889f2fe4b053308bb59a3cc02a0cd144d641/pillow-11.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:598174aef4589af795f66f9caab87ba4ff860ce08cd5bb447c6fc553ffee603c", size = 4669390 }, + { url = "https://files.pythonhosted.org/packages/96/ae/ca0099a3995976a9fce2f423166f7bff9b12244afdc7520f6ed38911539a/pillow-11.2.1-cp312-cp312-win32.whl", hash = "sha256:1d535df14716e7f8776b9e7fee118576d65572b4aad3ed639be9e4fa88a1cad3", size = 2332309 }, + { url = "https://files.pythonhosted.org/packages/7c/18/24bff2ad716257fc03da964c5e8f05d9790a779a8895d6566e493ccf0189/pillow-11.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:14e33b28bf17c7a38eede290f77db7c664e4eb01f7869e37fa98a5aa95978941", size = 2676768 }, + { url = "https://files.pythonhosted.org/packages/da/bb/e8d656c9543276517ee40184aaa39dcb41e683bca121022f9323ae11b39d/pillow-11.2.1-cp312-cp312-win_arm64.whl", hash = "sha256:21e1470ac9e5739ff880c211fc3af01e3ae505859392bf65458c224d0bf283eb", size = 2415087 }, + { url = "https://files.pythonhosted.org/packages/36/9c/447528ee3776e7ab8897fe33697a7ff3f0475bb490c5ac1456a03dc57956/pillow-11.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:fdec757fea0b793056419bca3e9932eb2b0ceec90ef4813ea4c1e072c389eb28", size = 3190098 }, + { url = "https://files.pythonhosted.org/packages/b5/09/29d5cd052f7566a63e5b506fac9c60526e9ecc553825551333e1e18a4858/pillow-11.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b0e130705d568e2f43a17bcbe74d90958e8a16263868a12c3e0d9c8162690830", size = 3030166 }, + { url = "https://files.pythonhosted.org/packages/71/5d/446ee132ad35e7600652133f9c2840b4799bbd8e4adba881284860da0a36/pillow-11.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bdb5e09068332578214cadd9c05e3d64d99e0e87591be22a324bdbc18925be0", size = 4408674 }, + { url = "https://files.pythonhosted.org/packages/69/5f/cbe509c0ddf91cc3a03bbacf40e5c2339c4912d16458fcb797bb47bcb269/pillow-11.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d189ba1bebfbc0c0e529159631ec72bb9e9bc041f01ec6d3233d6d82eb823bc1", size = 4496005 }, + { url = "https://files.pythonhosted.org/packages/f9/b3/dd4338d8fb8a5f312021f2977fb8198a1184893f9b00b02b75d565c33b51/pillow-11.2.1-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:191955c55d8a712fab8934a42bfefbf99dd0b5875078240943f913bb66d46d9f", size = 4518707 }, + { url = "https://files.pythonhosted.org/packages/13/eb/2552ecebc0b887f539111c2cd241f538b8ff5891b8903dfe672e997529be/pillow-11.2.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:ad275964d52e2243430472fc5d2c2334b4fc3ff9c16cb0a19254e25efa03a155", size = 4610008 }, + { url = "https://files.pythonhosted.org/packages/72/d1/924ce51bea494cb6e7959522d69d7b1c7e74f6821d84c63c3dc430cbbf3b/pillow-11.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:750f96efe0597382660d8b53e90dd1dd44568a8edb51cb7f9d5d918b80d4de14", size = 4585420 }, + { url = "https://files.pythonhosted.org/packages/43/ab/8f81312d255d713b99ca37479a4cb4b0f48195e530cdc1611990eb8fd04b/pillow-11.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fe15238d3798788d00716637b3d4e7bb6bde18b26e5d08335a96e88564a36b6b", size = 4667655 }, + { url = "https://files.pythonhosted.org/packages/94/86/8f2e9d2dc3d308dfd137a07fe1cc478df0a23d42a6c4093b087e738e4827/pillow-11.2.1-cp313-cp313-win32.whl", hash = "sha256:3fe735ced9a607fee4f481423a9c36701a39719252a9bb251679635f99d0f7d2", size = 2332329 }, + { url = "https://files.pythonhosted.org/packages/6d/ec/1179083b8d6067a613e4d595359b5fdea65d0a3b7ad623fee906e1b3c4d2/pillow-11.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:74ee3d7ecb3f3c05459ba95eed5efa28d6092d751ce9bf20e3e253a4e497e691", size = 2676388 }, + { url = "https://files.pythonhosted.org/packages/23/f1/2fc1e1e294de897df39fa8622d829b8828ddad938b0eaea256d65b84dd72/pillow-11.2.1-cp313-cp313-win_arm64.whl", hash = "sha256:5119225c622403afb4b44bad4c1ca6c1f98eed79db8d3bc6e4e160fc6339d66c", size = 2414950 }, + { url = "https://files.pythonhosted.org/packages/c4/3e/c328c48b3f0ead7bab765a84b4977acb29f101d10e4ef57a5e3400447c03/pillow-11.2.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:8ce2e8411c7aaef53e6bb29fe98f28cd4fbd9a1d9be2eeea434331aac0536b22", size = 3192759 }, + { url = "https://files.pythonhosted.org/packages/18/0e/1c68532d833fc8b9f404d3a642991441d9058eccd5606eab31617f29b6d4/pillow-11.2.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:9ee66787e095127116d91dea2143db65c7bb1e232f617aa5957c0d9d2a3f23a7", size = 3033284 }, + { url = "https://files.pythonhosted.org/packages/b7/cb/6faf3fb1e7705fd2db74e070f3bf6f88693601b0ed8e81049a8266de4754/pillow-11.2.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9622e3b6c1d8b551b6e6f21873bdcc55762b4b2126633014cea1803368a9aa16", size = 4445826 }, + { url = "https://files.pythonhosted.org/packages/07/94/8be03d50b70ca47fb434a358919d6a8d6580f282bbb7af7e4aa40103461d/pillow-11.2.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63b5dff3a68f371ea06025a1a6966c9a1e1ee452fc8020c2cd0ea41b83e9037b", size = 4527329 }, + { url = "https://files.pythonhosted.org/packages/fd/a4/bfe78777076dc405e3bd2080bc32da5ab3945b5a25dc5d8acaa9de64a162/pillow-11.2.1-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:31df6e2d3d8fc99f993fd253e97fae451a8db2e7207acf97859732273e108406", size = 4549049 }, + { url = "https://files.pythonhosted.org/packages/65/4d/eaf9068dc687c24979e977ce5677e253624bd8b616b286f543f0c1b91662/pillow-11.2.1-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:062b7a42d672c45a70fa1f8b43d1d38ff76b63421cbbe7f88146b39e8a558d91", size = 4635408 }, + { url = "https://files.pythonhosted.org/packages/1d/26/0fd443365d9c63bc79feb219f97d935cd4b93af28353cba78d8e77b61719/pillow-11.2.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4eb92eca2711ef8be42fd3f67533765d9fd043b8c80db204f16c8ea62ee1a751", size = 4614863 }, + { url = "https://files.pythonhosted.org/packages/49/65/dca4d2506be482c2c6641cacdba5c602bc76d8ceb618fd37de855653a419/pillow-11.2.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f91ebf30830a48c825590aede79376cb40f110b387c17ee9bd59932c961044f9", size = 4692938 }, + { url = "https://files.pythonhosted.org/packages/b3/92/1ca0c3f09233bd7decf8f7105a1c4e3162fb9142128c74adad0fb361b7eb/pillow-11.2.1-cp313-cp313t-win32.whl", hash = "sha256:e0b55f27f584ed623221cfe995c912c61606be8513bfa0e07d2c674b4516d9dd", size = 2335774 }, + { url = "https://files.pythonhosted.org/packages/a5/ac/77525347cb43b83ae905ffe257bbe2cc6fd23acb9796639a1f56aa59d191/pillow-11.2.1-cp313-cp313t-win_amd64.whl", hash = "sha256:36d6b82164c39ce5482f649b437382c0fb2395eabc1e2b1702a6deb8ad647d6e", size = 2681895 }, + { url = "https://files.pythonhosted.org/packages/67/32/32dc030cfa91ca0fc52baebbba2e009bb001122a1daa8b6a79ad830b38d3/pillow-11.2.1-cp313-cp313t-win_arm64.whl", hash = "sha256:225c832a13326e34f212d2072982bb1adb210e0cc0b153e688743018c94a2681", size = 2417234 }, + { url = "https://files.pythonhosted.org/packages/a4/ad/2613c04633c7257d9481ab21d6b5364b59fc5d75faafd7cb8693523945a3/pillow-11.2.1-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:80f1df8dbe9572b4b7abdfa17eb5d78dd620b1d55d9e25f834efdbee872d3aed", size = 3181734 }, + { url = "https://files.pythonhosted.org/packages/a4/fd/dcdda4471ed667de57bb5405bb42d751e6cfdd4011a12c248b455c778e03/pillow-11.2.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:ea926cfbc3957090becbcbbb65ad177161a2ff2ad578b5a6ec9bb1e1cd78753c", size = 2999841 }, + { url = "https://files.pythonhosted.org/packages/ac/89/8a2536e95e77432833f0db6fd72a8d310c8e4272a04461fb833eb021bf94/pillow-11.2.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:738db0e0941ca0376804d4de6a782c005245264edaa253ffce24e5a15cbdc7bd", size = 3437470 }, + { url = "https://files.pythonhosted.org/packages/9d/8f/abd47b73c60712f88e9eda32baced7bfc3e9bd6a7619bb64b93acff28c3e/pillow-11.2.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9db98ab6565c69082ec9b0d4e40dd9f6181dab0dd236d26f7a50b8b9bfbd5076", size = 3460013 }, + { url = "https://files.pythonhosted.org/packages/f6/20/5c0a0aa83b213b7a07ec01e71a3d6ea2cf4ad1d2c686cc0168173b6089e7/pillow-11.2.1-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:036e53f4170e270ddb8797d4c590e6dd14d28e15c7da375c18978045f7e6c37b", size = 3527165 }, + { url = "https://files.pythonhosted.org/packages/58/0e/2abab98a72202d91146abc839e10c14f7cf36166f12838ea0c4db3ca6ecb/pillow-11.2.1-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:14f73f7c291279bd65fda51ee87affd7c1e097709f7fdd0188957a16c264601f", size = 3571586 }, + { url = "https://files.pythonhosted.org/packages/21/2c/5e05f58658cf49b6667762cca03d6e7d85cededde2caf2ab37b81f80e574/pillow-11.2.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:208653868d5c9ecc2b327f9b9ef34e0e42a4cdd172c2988fd81d62d2bc9bc044", size = 2674751 }, +] + +[[package]] +name = "platformdirs" +version = "4.3.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fe/8b/3c73abc9c759ecd3f1f7ceff6685840859e8070c4d947c93fae71f6a0bf2/platformdirs-4.3.8.tar.gz", hash = "sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc", size = 21362 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fe/39/979e8e21520d4e47a0bbe349e2713c0aac6f3d853d0e5b34d76206c439aa/platformdirs-4.3.8-py3-none-any.whl", hash = "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4", size = 18567 }, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538 }, +] + +[[package]] +name = "portalocker" +version = "3.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pywin32", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5e/77/65b857a69ed876e1951e88aaba60f5ce6120c33703f7cb61a3c894b8c1b6/portalocker-3.2.0.tar.gz", hash = "sha256:1f3002956a54a8c3730586c5c77bf18fae4149e07eaf1c29fc3faf4d5a3f89ac", size = 95644 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4b/a6/38c8e2f318bf67d338f4d629e93b0b4b9af331f455f0390ea8ce4a099b26/portalocker-3.2.0-py3-none-any.whl", hash = "sha256:3cdc5f565312224bc570c49337bd21428bba0ef363bbcf58b9ef4a9f11779968", size = 22424 }, +] + +[[package]] +name = "prompt-toolkit" +version = "3.0.51" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "wcwidth" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bb/6e/9d084c929dfe9e3bfe0c6a47e31f78a25c54627d64a66e884a8bf5474f1c/prompt_toolkit-3.0.51.tar.gz", hash = "sha256:931a162e3b27fc90c86f1b48bb1fb2c528c2761475e57c9c06de13311c7b54ed", size = 428940 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ce/4f/5249960887b1fbe561d9ff265496d170b55a735b76724f10ef19f9e40716/prompt_toolkit-3.0.51-py3-none-any.whl", hash = "sha256:52742911fde84e2d423e2f9a4cf1de7d7ac4e51958f648d9540e0fb8db077b07", size = 387810 }, +] + +[[package]] +name = "propcache" +version = "0.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/07/c8/fdc6686a986feae3541ea23dcaa661bd93972d3940460646c6bb96e21c40/propcache-0.3.1.tar.gz", hash = "sha256:40d980c33765359098837527e18eddefc9a24cea5b45e078a7f3bb5b032c6ecf", size = 43651 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/90/0f/5a5319ee83bd651f75311fcb0c492c21322a7fc8f788e4eef23f44243427/propcache-0.3.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7f30241577d2fef2602113b70ef7231bf4c69a97e04693bde08ddab913ba0ce5", size = 80243 }, + { url = "https://files.pythonhosted.org/packages/ce/84/3db5537e0879942783e2256616ff15d870a11d7ac26541336fe1b673c818/propcache-0.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:43593c6772aa12abc3af7784bff4a41ffa921608dd38b77cf1dfd7f5c4e71371", size = 46503 }, + { url = "https://files.pythonhosted.org/packages/e2/c8/b649ed972433c3f0d827d7f0cf9ea47162f4ef8f4fe98c5f3641a0bc63ff/propcache-0.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a75801768bbe65499495660b777e018cbe90c7980f07f8aa57d6be79ea6f71da", size = 45934 }, + { url = "https://files.pythonhosted.org/packages/59/f9/4c0a5cf6974c2c43b1a6810c40d889769cc8f84cea676cbe1e62766a45f8/propcache-0.3.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f6f1324db48f001c2ca26a25fa25af60711e09b9aaf4b28488602776f4f9a744", size = 233633 }, + { url = "https://files.pythonhosted.org/packages/e7/64/66f2f4d1b4f0007c6e9078bd95b609b633d3957fe6dd23eac33ebde4b584/propcache-0.3.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cdb0f3e1eb6dfc9965d19734d8f9c481b294b5274337a8cb5cb01b462dcb7e0", size = 241124 }, + { url = "https://files.pythonhosted.org/packages/aa/bf/7b8c9fd097d511638fa9b6af3d986adbdf567598a567b46338c925144c1b/propcache-0.3.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1eb34d90aac9bfbced9a58b266f8946cb5935869ff01b164573a7634d39fbcb5", size = 240283 }, + { url = "https://files.pythonhosted.org/packages/fa/c9/e85aeeeaae83358e2a1ef32d6ff50a483a5d5248bc38510d030a6f4e2816/propcache-0.3.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f35c7070eeec2cdaac6fd3fe245226ed2a6292d3ee8c938e5bb645b434c5f256", size = 232498 }, + { url = "https://files.pythonhosted.org/packages/8e/66/acb88e1f30ef5536d785c283af2e62931cb934a56a3ecf39105887aa8905/propcache-0.3.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b23c11c2c9e6d4e7300c92e022046ad09b91fd00e36e83c44483df4afa990073", size = 221486 }, + { url = "https://files.pythonhosted.org/packages/f5/f9/233ddb05ffdcaee4448508ee1d70aa7deff21bb41469ccdfcc339f871427/propcache-0.3.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3e19ea4ea0bf46179f8a3652ac1426e6dcbaf577ce4b4f65be581e237340420d", size = 222675 }, + { url = "https://files.pythonhosted.org/packages/98/b8/eb977e28138f9e22a5a789daf608d36e05ed93093ef12a12441030da800a/propcache-0.3.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:bd39c92e4c8f6cbf5f08257d6360123af72af9f4da75a690bef50da77362d25f", size = 215727 }, + { url = "https://files.pythonhosted.org/packages/89/2d/5f52d9c579f67b8ee1edd9ec073c91b23cc5b7ff7951a1e449e04ed8fdf3/propcache-0.3.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:b0313e8b923b3814d1c4a524c93dfecea5f39fa95601f6a9b1ac96cd66f89ea0", size = 217878 }, + { url = "https://files.pythonhosted.org/packages/7a/fd/5283e5ed8a82b00c7a989b99bb6ea173db1ad750bf0bf8dff08d3f4a4e28/propcache-0.3.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e861ad82892408487be144906a368ddbe2dc6297074ade2d892341b35c59844a", size = 230558 }, + { url = "https://files.pythonhosted.org/packages/90/38/ab17d75938ef7ac87332c588857422ae126b1c76253f0f5b1242032923ca/propcache-0.3.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:61014615c1274df8da5991a1e5da85a3ccb00c2d4701ac6f3383afd3ca47ab0a", size = 233754 }, + { url = "https://files.pythonhosted.org/packages/06/5d/3b921b9c60659ae464137508d3b4c2b3f52f592ceb1964aa2533b32fcf0b/propcache-0.3.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:71ebe3fe42656a2328ab08933d420df5f3ab121772eef78f2dc63624157f0ed9", size = 226088 }, + { url = "https://files.pythonhosted.org/packages/54/6e/30a11f4417d9266b5a464ac5a8c5164ddc9dd153dfa77bf57918165eb4ae/propcache-0.3.1-cp311-cp311-win32.whl", hash = "sha256:58aa11f4ca8b60113d4b8e32d37e7e78bd8af4d1a5b5cb4979ed856a45e62005", size = 40859 }, + { url = "https://files.pythonhosted.org/packages/1d/3a/8a68dd867da9ca2ee9dfd361093e9cb08cb0f37e5ddb2276f1b5177d7731/propcache-0.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:9532ea0b26a401264b1365146c440a6d78269ed41f83f23818d4b79497aeabe7", size = 45153 }, + { url = "https://files.pythonhosted.org/packages/41/aa/ca78d9be314d1e15ff517b992bebbed3bdfef5b8919e85bf4940e57b6137/propcache-0.3.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:f78eb8422acc93d7b69964012ad7048764bb45a54ba7a39bb9e146c72ea29723", size = 80430 }, + { url = "https://files.pythonhosted.org/packages/1a/d8/f0c17c44d1cda0ad1979af2e593ea290defdde9eaeb89b08abbe02a5e8e1/propcache-0.3.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:89498dd49c2f9a026ee057965cdf8192e5ae070ce7d7a7bd4b66a8e257d0c976", size = 46637 }, + { url = "https://files.pythonhosted.org/packages/ae/bd/c1e37265910752e6e5e8a4c1605d0129e5b7933c3dc3cf1b9b48ed83b364/propcache-0.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:09400e98545c998d57d10035ff623266927cb784d13dd2b31fd33b8a5316b85b", size = 46123 }, + { url = "https://files.pythonhosted.org/packages/d4/b0/911eda0865f90c0c7e9f0415d40a5bf681204da5fd7ca089361a64c16b28/propcache-0.3.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa8efd8c5adc5a2c9d3b952815ff8f7710cefdcaf5f2c36d26aff51aeca2f12f", size = 243031 }, + { url = "https://files.pythonhosted.org/packages/0a/06/0da53397c76a74271621807265b6eb61fb011451b1ddebf43213df763669/propcache-0.3.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c2fe5c910f6007e716a06d269608d307b4f36e7babee5f36533722660e8c4a70", size = 249100 }, + { url = "https://files.pythonhosted.org/packages/f1/eb/13090e05bf6b963fc1653cdc922133ced467cb4b8dab53158db5a37aa21e/propcache-0.3.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a0ab8cf8cdd2194f8ff979a43ab43049b1df0b37aa64ab7eca04ac14429baeb7", size = 250170 }, + { url = "https://files.pythonhosted.org/packages/3b/4c/f72c9e1022b3b043ec7dc475a0f405d4c3e10b9b1d378a7330fecf0652da/propcache-0.3.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:563f9d8c03ad645597b8d010ef4e9eab359faeb11a0a2ac9f7b4bc8c28ebef25", size = 245000 }, + { url = "https://files.pythonhosted.org/packages/e8/fd/970ca0e22acc829f1adf5de3724085e778c1ad8a75bec010049502cb3a86/propcache-0.3.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fb6e0faf8cb6b4beea5d6ed7b5a578254c6d7df54c36ccd3d8b3eb00d6770277", size = 230262 }, + { url = "https://files.pythonhosted.org/packages/c4/42/817289120c6b9194a44f6c3e6b2c3277c5b70bbad39e7df648f177cc3634/propcache-0.3.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1c5c7ab7f2bb3f573d1cb921993006ba2d39e8621019dffb1c5bc94cdbae81e8", size = 236772 }, + { url = "https://files.pythonhosted.org/packages/7c/9c/3b3942b302badd589ad6b672da3ca7b660a6c2f505cafd058133ddc73918/propcache-0.3.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:050b571b2e96ec942898f8eb46ea4bfbb19bd5502424747e83badc2d4a99a44e", size = 231133 }, + { url = "https://files.pythonhosted.org/packages/98/a1/75f6355f9ad039108ff000dfc2e19962c8dea0430da9a1428e7975cf24b2/propcache-0.3.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e1c4d24b804b3a87e9350f79e2371a705a188d292fd310e663483af6ee6718ee", size = 230741 }, + { url = "https://files.pythonhosted.org/packages/67/0c/3e82563af77d1f8731132166da69fdfd95e71210e31f18edce08a1eb11ea/propcache-0.3.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:e4fe2a6d5ce975c117a6bb1e8ccda772d1e7029c1cca1acd209f91d30fa72815", size = 244047 }, + { url = "https://files.pythonhosted.org/packages/f7/50/9fb7cca01532a08c4d5186d7bb2da6c4c587825c0ae134b89b47c7d62628/propcache-0.3.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:feccd282de1f6322f56f6845bf1207a537227812f0a9bf5571df52bb418d79d5", size = 246467 }, + { url = "https://files.pythonhosted.org/packages/a9/02/ccbcf3e1c604c16cc525309161d57412c23cf2351523aedbb280eb7c9094/propcache-0.3.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ec314cde7314d2dd0510c6787326bbffcbdc317ecee6b7401ce218b3099075a7", size = 241022 }, + { url = "https://files.pythonhosted.org/packages/db/19/e777227545e09ca1e77a6e21274ae9ec45de0f589f0ce3eca2a41f366220/propcache-0.3.1-cp312-cp312-win32.whl", hash = "sha256:7d2d5a0028d920738372630870e7d9644ce437142197f8c827194fca404bf03b", size = 40647 }, + { url = "https://files.pythonhosted.org/packages/24/bb/3b1b01da5dd04c77a204c84e538ff11f624e31431cfde7201d9110b092b1/propcache-0.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:88c423efef9d7a59dae0614eaed718449c09a5ac79a5f224a8b9664d603f04a3", size = 44784 }, + { url = "https://files.pythonhosted.org/packages/58/60/f645cc8b570f99be3cf46714170c2de4b4c9d6b827b912811eff1eb8a412/propcache-0.3.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f1528ec4374617a7a753f90f20e2f551121bb558fcb35926f99e3c42367164b8", size = 77865 }, + { url = "https://files.pythonhosted.org/packages/6f/d4/c1adbf3901537582e65cf90fd9c26fde1298fde5a2c593f987112c0d0798/propcache-0.3.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:dc1915ec523b3b494933b5424980831b636fe483d7d543f7afb7b3bf00f0c10f", size = 45452 }, + { url = "https://files.pythonhosted.org/packages/d1/b5/fe752b2e63f49f727c6c1c224175d21b7d1727ce1d4873ef1c24c9216830/propcache-0.3.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a110205022d077da24e60b3df8bcee73971be9575dec5573dd17ae5d81751111", size = 44800 }, + { url = "https://files.pythonhosted.org/packages/62/37/fc357e345bc1971e21f76597028b059c3d795c5ca7690d7a8d9a03c9708a/propcache-0.3.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d249609e547c04d190e820d0d4c8ca03ed4582bcf8e4e160a6969ddfb57b62e5", size = 225804 }, + { url = "https://files.pythonhosted.org/packages/0d/f1/16e12c33e3dbe7f8b737809bad05719cff1dccb8df4dafbcff5575002c0e/propcache-0.3.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ced33d827625d0a589e831126ccb4f5c29dfdf6766cac441d23995a65825dcb", size = 230650 }, + { url = "https://files.pythonhosted.org/packages/3e/a2/018b9f2ed876bf5091e60153f727e8f9073d97573f790ff7cdf6bc1d1fb8/propcache-0.3.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4114c4ada8f3181af20808bedb250da6bae56660e4b8dfd9cd95d4549c0962f7", size = 234235 }, + { url = "https://files.pythonhosted.org/packages/45/5f/3faee66fc930dfb5da509e34c6ac7128870631c0e3582987fad161fcb4b1/propcache-0.3.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:975af16f406ce48f1333ec5e912fe11064605d5c5b3f6746969077cc3adeb120", size = 228249 }, + { url = "https://files.pythonhosted.org/packages/62/1e/a0d5ebda5da7ff34d2f5259a3e171a94be83c41eb1e7cd21a2105a84a02e/propcache-0.3.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a34aa3a1abc50740be6ac0ab9d594e274f59960d3ad253cd318af76b996dd654", size = 214964 }, + { url = "https://files.pythonhosted.org/packages/db/a0/d72da3f61ceab126e9be1f3bc7844b4e98c6e61c985097474668e7e52152/propcache-0.3.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9cec3239c85ed15bfaded997773fdad9fb5662b0a7cbc854a43f291eb183179e", size = 222501 }, + { url = "https://files.pythonhosted.org/packages/18/6d/a008e07ad7b905011253adbbd97e5b5375c33f0b961355ca0a30377504ac/propcache-0.3.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:05543250deac8e61084234d5fc54f8ebd254e8f2b39a16b1dce48904f45b744b", size = 217917 }, + { url = "https://files.pythonhosted.org/packages/98/37/02c9343ffe59e590e0e56dc5c97d0da2b8b19fa747ebacf158310f97a79a/propcache-0.3.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:5cb5918253912e088edbf023788de539219718d3b10aef334476b62d2b53de53", size = 217089 }, + { url = "https://files.pythonhosted.org/packages/53/1b/d3406629a2c8a5666d4674c50f757a77be119b113eedd47b0375afdf1b42/propcache-0.3.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f3bbecd2f34d0e6d3c543fdb3b15d6b60dd69970c2b4c822379e5ec8f6f621d5", size = 228102 }, + { url = "https://files.pythonhosted.org/packages/cd/a7/3664756cf50ce739e5f3abd48febc0be1a713b1f389a502ca819791a6b69/propcache-0.3.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:aca63103895c7d960a5b9b044a83f544b233c95e0dcff114389d64d762017af7", size = 230122 }, + { url = "https://files.pythonhosted.org/packages/35/36/0bbabaacdcc26dac4f8139625e930f4311864251276033a52fd52ff2a274/propcache-0.3.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5a0a9898fdb99bf11786265468571e628ba60af80dc3f6eb89a3545540c6b0ef", size = 226818 }, + { url = "https://files.pythonhosted.org/packages/cc/27/4e0ef21084b53bd35d4dae1634b6d0bad35e9c58ed4f032511acca9d4d26/propcache-0.3.1-cp313-cp313-win32.whl", hash = "sha256:3a02a28095b5e63128bcae98eb59025924f121f048a62393db682f049bf4ac24", size = 40112 }, + { url = "https://files.pythonhosted.org/packages/a6/2c/a54614d61895ba6dd7ac8f107e2b2a0347259ab29cbf2ecc7b94fa38c4dc/propcache-0.3.1-cp313-cp313-win_amd64.whl", hash = "sha256:813fbb8b6aea2fc9659815e585e548fe706d6f663fa73dff59a1677d4595a037", size = 44034 }, + { url = "https://files.pythonhosted.org/packages/5a/a8/0a4fd2f664fc6acc66438370905124ce62e84e2e860f2557015ee4a61c7e/propcache-0.3.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:a444192f20f5ce8a5e52761a031b90f5ea6288b1eef42ad4c7e64fef33540b8f", size = 82613 }, + { url = "https://files.pythonhosted.org/packages/4d/e5/5ef30eb2cd81576256d7b6caaa0ce33cd1d2c2c92c8903cccb1af1a4ff2f/propcache-0.3.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0fbe94666e62ebe36cd652f5fc012abfbc2342de99b523f8267a678e4dfdee3c", size = 47763 }, + { url = "https://files.pythonhosted.org/packages/87/9a/87091ceb048efeba4d28e903c0b15bcc84b7c0bf27dc0261e62335d9b7b8/propcache-0.3.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f011f104db880f4e2166bcdcf7f58250f7a465bc6b068dc84c824a3d4a5c94dc", size = 47175 }, + { url = "https://files.pythonhosted.org/packages/3e/2f/854e653c96ad1161f96194c6678a41bbb38c7947d17768e8811a77635a08/propcache-0.3.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e584b6d388aeb0001d6d5c2bd86b26304adde6d9bb9bfa9c4889805021b96de", size = 292265 }, + { url = "https://files.pythonhosted.org/packages/40/8d/090955e13ed06bc3496ba4a9fb26c62e209ac41973cb0d6222de20c6868f/propcache-0.3.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8a17583515a04358b034e241f952f1715243482fc2c2945fd99a1b03a0bd77d6", size = 294412 }, + { url = "https://files.pythonhosted.org/packages/39/e6/d51601342e53cc7582449e6a3c14a0479fab2f0750c1f4d22302e34219c6/propcache-0.3.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5aed8d8308215089c0734a2af4f2e95eeb360660184ad3912686c181e500b2e7", size = 294290 }, + { url = "https://files.pythonhosted.org/packages/3b/4d/be5f1a90abc1881884aa5878989a1acdafd379a91d9c7e5e12cef37ec0d7/propcache-0.3.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d8e309ff9a0503ef70dc9a0ebd3e69cf7b3894c9ae2ae81fc10943c37762458", size = 282926 }, + { url = "https://files.pythonhosted.org/packages/57/2b/8f61b998c7ea93a2b7eca79e53f3e903db1787fca9373af9e2cf8dc22f9d/propcache-0.3.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b655032b202028a582d27aeedc2e813299f82cb232f969f87a4fde491a233f11", size = 267808 }, + { url = "https://files.pythonhosted.org/packages/11/1c/311326c3dfce59c58a6098388ba984b0e5fb0381ef2279ec458ef99bd547/propcache-0.3.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9f64d91b751df77931336b5ff7bafbe8845c5770b06630e27acd5dbb71e1931c", size = 290916 }, + { url = "https://files.pythonhosted.org/packages/4b/74/91939924b0385e54dc48eb2e4edd1e4903ffd053cf1916ebc5347ac227f7/propcache-0.3.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:19a06db789a4bd896ee91ebc50d059e23b3639c25d58eb35be3ca1cbe967c3bf", size = 262661 }, + { url = "https://files.pythonhosted.org/packages/c2/d7/e6079af45136ad325c5337f5dd9ef97ab5dc349e0ff362fe5c5db95e2454/propcache-0.3.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:bef100c88d8692864651b5f98e871fb090bd65c8a41a1cb0ff2322db39c96c27", size = 264384 }, + { url = "https://files.pythonhosted.org/packages/b7/d5/ba91702207ac61ae6f1c2da81c5d0d6bf6ce89e08a2b4d44e411c0bbe867/propcache-0.3.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:87380fb1f3089d2a0b8b00f006ed12bd41bd858fabfa7330c954c70f50ed8757", size = 291420 }, + { url = "https://files.pythonhosted.org/packages/58/70/2117780ed7edcd7ba6b8134cb7802aada90b894a9810ec56b7bb6018bee7/propcache-0.3.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e474fc718e73ba5ec5180358aa07f6aded0ff5f2abe700e3115c37d75c947e18", size = 290880 }, + { url = "https://files.pythonhosted.org/packages/4a/1f/ecd9ce27710021ae623631c0146719280a929d895a095f6d85efb6a0be2e/propcache-0.3.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:17d1c688a443355234f3c031349da69444be052613483f3e4158eef751abcd8a", size = 287407 }, + { url = "https://files.pythonhosted.org/packages/3e/66/2e90547d6b60180fb29e23dc87bd8c116517d4255240ec6d3f7dc23d1926/propcache-0.3.1-cp313-cp313t-win32.whl", hash = "sha256:359e81a949a7619802eb601d66d37072b79b79c2505e6d3fd8b945538411400d", size = 42573 }, + { url = "https://files.pythonhosted.org/packages/cb/8f/50ad8599399d1861b4d2b6b45271f0ef6af1b09b0a2386a46dbaf19c9535/propcache-0.3.1-cp313-cp313t-win_amd64.whl", hash = "sha256:e7fb9a84c9abbf2b2683fa3e7b0d7da4d8ecf139a1c635732a8bda29c5214b0e", size = 46757 }, + { url = "https://files.pythonhosted.org/packages/b8/d3/c3cb8f1d6ae3b37f83e1de806713a9b3642c5895f0215a62e1a4bd6e5e34/propcache-0.3.1-py3-none-any.whl", hash = "sha256:9a8ecf38de50a7f518c21568c80f985e776397b902f1ce0b01f799aba1608b40", size = 12376 }, +] + +[[package]] +name = "protobuf" +version = "6.31.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/52/f3/b9655a711b32c19720253f6f06326faf90580834e2e83f840472d752bc8b/protobuf-6.31.1.tar.gz", hash = "sha256:d8cac4c982f0b957a4dc73a80e2ea24fab08e679c0de9deb835f4a12d69aca9a", size = 441797 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f3/6f/6ab8e4bf962fd5570d3deaa2d5c38f0a363f57b4501047b5ebeb83ab1125/protobuf-6.31.1-cp310-abi3-win32.whl", hash = "sha256:7fa17d5a29c2e04b7d90e5e32388b8bfd0e7107cd8e616feef7ed3fa6bdab5c9", size = 423603 }, + { url = "https://files.pythonhosted.org/packages/44/3a/b15c4347dd4bf3a1b0ee882f384623e2063bb5cf9fa9d57990a4f7df2fb6/protobuf-6.31.1-cp310-abi3-win_amd64.whl", hash = "sha256:426f59d2964864a1a366254fa703b8632dcec0790d8862d30034d8245e1cd447", size = 435283 }, + { url = "https://files.pythonhosted.org/packages/6a/c9/b9689a2a250264a84e66c46d8862ba788ee7a641cdca39bccf64f59284b7/protobuf-6.31.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:6f1227473dc43d44ed644425268eb7c2e488ae245d51c6866d19fe158e207402", size = 425604 }, + { url = "https://files.pythonhosted.org/packages/76/a1/7a5a94032c83375e4fe7e7f56e3976ea6ac90c5e85fac8576409e25c39c3/protobuf-6.31.1-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:a40fc12b84c154884d7d4c4ebd675d5b3b5283e155f324049ae396b95ddebc39", size = 322115 }, + { url = "https://files.pythonhosted.org/packages/fa/b1/b59d405d64d31999244643d88c45c8241c58f17cc887e73bcb90602327f8/protobuf-6.31.1-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:4ee898bf66f7a8b0bd21bce523814e6fbd8c6add948045ce958b73af7e8878c6", size = 321070 }, + { url = "https://files.pythonhosted.org/packages/f7/af/ab3c51ab7507a7325e98ffe691d9495ee3d3aa5f589afad65ec920d39821/protobuf-6.31.1-py3-none-any.whl", hash = "sha256:720a6c7e6b77288b85063569baae8536671b39f15cc22037ec7045658d80489e", size = 168724 }, +] + +[[package]] +name = "psutil" +version = "7.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2a/80/336820c1ad9286a4ded7e845b2eccfcb27851ab8ac6abece774a6ff4d3de/psutil-7.0.0.tar.gz", hash = "sha256:7be9c3eba38beccb6495ea33afd982a44074b78f28c434a1f51cc07fd315c456", size = 497003 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ed/e6/2d26234410f8b8abdbf891c9da62bee396583f713fb9f3325a4760875d22/psutil-7.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:101d71dc322e3cffd7cea0650b09b3d08b8e7c4109dd6809fe452dfd00e58b25", size = 238051 }, + { url = "https://files.pythonhosted.org/packages/04/8b/30f930733afe425e3cbfc0e1468a30a18942350c1a8816acfade80c005c4/psutil-7.0.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:39db632f6bb862eeccf56660871433e111b6ea58f2caea825571951d4b6aa3da", size = 239535 }, + { url = "https://files.pythonhosted.org/packages/2a/ed/d362e84620dd22876b55389248e522338ed1bf134a5edd3b8231d7207f6d/psutil-7.0.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fcee592b4c6f146991ca55919ea3d1f8926497a713ed7faaf8225e174581e91", size = 275004 }, + { url = "https://files.pythonhosted.org/packages/bf/b9/b0eb3f3cbcb734d930fdf839431606844a825b23eaf9a6ab371edac8162c/psutil-7.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b1388a4f6875d7e2aff5c4ca1cc16c545ed41dd8bb596cefea80111db353a34", size = 277986 }, + { url = "https://files.pythonhosted.org/packages/eb/a2/709e0fe2f093556c17fbafda93ac032257242cabcc7ff3369e2cb76a97aa/psutil-7.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5f098451abc2828f7dc6b58d44b532b22f2088f4999a937557b603ce72b1993", size = 279544 }, + { url = "https://files.pythonhosted.org/packages/50/e6/eecf58810b9d12e6427369784efe814a1eec0f492084ce8eb8f4d89d6d61/psutil-7.0.0-cp37-abi3-win32.whl", hash = "sha256:ba3fcef7523064a6c9da440fc4d6bd07da93ac726b5733c29027d7dc95b39d99", size = 241053 }, + { url = "https://files.pythonhosted.org/packages/50/1b/6921afe68c74868b4c9fa424dad3be35b095e16687989ebbb50ce4fceb7c/psutil-7.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:4cf3d4eb1aa9b348dec30105c55cd9b7d4629285735a102beb4441e38db90553", size = 244885 }, +] + +[[package]] +name = "ptyprocess" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/20/e5/16ff212c1e452235a90aeb09066144d0c5a6a8c0834397e03f5224495c4e/ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220", size = 70762 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35", size = 13993 }, +] + +[[package]] +name = "pure-eval" +version = "0.2.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/05/0a34433a064256a578f1783a10da6df098ceaa4a57bbeaa96a6c0352786b/pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42", size = 19752 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0", size = 11842 }, +] + +[[package]] +name = "pycparser" +version = "2.22" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1d/b2/31537cf4b1ca988837256c910a668b553fceb8f069bedc4b1c826024b52c/pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6", size = 172736 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/13/a3/a812df4e2dd5696d1f351d58b8fe16a405b234ad2886a0dab9183fb78109/pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc", size = 117552 }, +] + +[[package]] +name = "pydantic" +version = "2.11.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-types" }, + { name = "pydantic-core" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f0/86/8ce9040065e8f924d642c58e4a344e33163a07f6b57f836d0d734e0ad3fb/pydantic-2.11.5.tar.gz", hash = "sha256:7f853db3d0ce78ce8bbb148c401c2cdd6431b3473c0cdff2755c7690952a7b7a", size = 787102 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b5/69/831ed22b38ff9b4b64b66569f0e5b7b97cf3638346eb95a2147fdb49ad5f/pydantic-2.11.5-py3-none-any.whl", hash = "sha256:f9c26ba06f9747749ca1e5c94d6a85cb84254577553c8785576fd38fa64dc0f7", size = 444229 }, +] + +[[package]] +name = "pydantic-core" +version = "2.33.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ad/88/5f2260bdfae97aabf98f1778d43f69574390ad787afb646292a638c923d4/pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc", size = 435195 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3f/8d/71db63483d518cbbf290261a1fc2839d17ff89fce7089e08cad07ccfce67/pydantic_core-2.33.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4c5b0a576fb381edd6d27f0a85915c6daf2f8138dc5c267a57c08a62900758c7", size = 2028584 }, + { url = "https://files.pythonhosted.org/packages/24/2f/3cfa7244ae292dd850989f328722d2aef313f74ffc471184dc509e1e4e5a/pydantic_core-2.33.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e799c050df38a639db758c617ec771fd8fb7a5f8eaaa4b27b101f266b216a246", size = 1855071 }, + { url = "https://files.pythonhosted.org/packages/b3/d3/4ae42d33f5e3f50dd467761304be2fa0a9417fbf09735bc2cce003480f2a/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc46a01bf8d62f227d5ecee74178ffc448ff4e5197c756331f71efcc66dc980f", size = 1897823 }, + { url = "https://files.pythonhosted.org/packages/f4/f3/aa5976e8352b7695ff808599794b1fba2a9ae2ee954a3426855935799488/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a144d4f717285c6d9234a66778059f33a89096dfb9b39117663fd8413d582dcc", size = 1983792 }, + { url = "https://files.pythonhosted.org/packages/d5/7a/cda9b5a23c552037717f2b2a5257e9b2bfe45e687386df9591eff7b46d28/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73cf6373c21bc80b2e0dc88444f41ae60b2f070ed02095754eb5a01df12256de", size = 2136338 }, + { url = "https://files.pythonhosted.org/packages/2b/9f/b8f9ec8dd1417eb9da784e91e1667d58a2a4a7b7b34cf4af765ef663a7e5/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3dc625f4aa79713512d1976fe9f0bc99f706a9dee21dfd1810b4bbbf228d0e8a", size = 2730998 }, + { url = "https://files.pythonhosted.org/packages/47/bc/cd720e078576bdb8255d5032c5d63ee5c0bf4b7173dd955185a1d658c456/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b21b5549499972441da4758d662aeea93f1923f953e9cbaff14b8b9565aef", size = 2003200 }, + { url = "https://files.pythonhosted.org/packages/ca/22/3602b895ee2cd29d11a2b349372446ae9727c32e78a94b3d588a40fdf187/pydantic_core-2.33.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bdc25f3681f7b78572699569514036afe3c243bc3059d3942624e936ec93450e", size = 2113890 }, + { url = "https://files.pythonhosted.org/packages/ff/e6/e3c5908c03cf00d629eb38393a98fccc38ee0ce8ecce32f69fc7d7b558a7/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fe5b32187cbc0c862ee201ad66c30cf218e5ed468ec8dc1cf49dec66e160cc4d", size = 2073359 }, + { url = "https://files.pythonhosted.org/packages/12/e7/6a36a07c59ebefc8777d1ffdaf5ae71b06b21952582e4b07eba88a421c79/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:bc7aee6f634a6f4a95676fcb5d6559a2c2a390330098dba5e5a5f28a2e4ada30", size = 2245883 }, + { url = "https://files.pythonhosted.org/packages/16/3f/59b3187aaa6cc0c1e6616e8045b284de2b6a87b027cce2ffcea073adf1d2/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:235f45e5dbcccf6bd99f9f472858849f73d11120d76ea8707115415f8e5ebebf", size = 2241074 }, + { url = "https://files.pythonhosted.org/packages/e0/ed/55532bb88f674d5d8f67ab121a2a13c385df382de2a1677f30ad385f7438/pydantic_core-2.33.2-cp311-cp311-win32.whl", hash = "sha256:6368900c2d3ef09b69cb0b913f9f8263b03786e5b2a387706c5afb66800efd51", size = 1910538 }, + { url = "https://files.pythonhosted.org/packages/fe/1b/25b7cccd4519c0b23c2dd636ad39d381abf113085ce4f7bec2b0dc755eb1/pydantic_core-2.33.2-cp311-cp311-win_amd64.whl", hash = "sha256:1e063337ef9e9820c77acc768546325ebe04ee38b08703244c1309cccc4f1bab", size = 1952909 }, + { url = "https://files.pythonhosted.org/packages/49/a9/d809358e49126438055884c4366a1f6227f0f84f635a9014e2deb9b9de54/pydantic_core-2.33.2-cp311-cp311-win_arm64.whl", hash = "sha256:6b99022f1d19bc32a4c2a0d544fc9a76e3be90f0b3f4af413f87d38749300e65", size = 1897786 }, + { url = "https://files.pythonhosted.org/packages/18/8a/2b41c97f554ec8c71f2a8a5f85cb56a8b0956addfe8b0efb5b3d77e8bdc3/pydantic_core-2.33.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a7ec89dc587667f22b6a0b6579c249fca9026ce7c333fc142ba42411fa243cdc", size = 2009000 }, + { url = "https://files.pythonhosted.org/packages/a1/02/6224312aacb3c8ecbaa959897af57181fb6cf3a3d7917fd44d0f2917e6f2/pydantic_core-2.33.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3c6db6e52c6d70aa0d00d45cdb9b40f0433b96380071ea80b09277dba021ddf7", size = 1847996 }, + { url = "https://files.pythonhosted.org/packages/d6/46/6dcdf084a523dbe0a0be59d054734b86a981726f221f4562aed313dbcb49/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e61206137cbc65e6d5256e1166f88331d3b6238e082d9f74613b9b765fb9025", size = 1880957 }, + { url = "https://files.pythonhosted.org/packages/ec/6b/1ec2c03837ac00886ba8160ce041ce4e325b41d06a034adbef11339ae422/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb8c529b2819c37140eb51b914153063d27ed88e3bdc31b71198a198e921e011", size = 1964199 }, + { url = "https://files.pythonhosted.org/packages/2d/1d/6bf34d6adb9debd9136bd197ca72642203ce9aaaa85cfcbfcf20f9696e83/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c52b02ad8b4e2cf14ca7b3d918f3eb0ee91e63b3167c32591e57c4317e134f8f", size = 2120296 }, + { url = "https://files.pythonhosted.org/packages/e0/94/2bd0aaf5a591e974b32a9f7123f16637776c304471a0ab33cf263cf5591a/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96081f1605125ba0855dfda83f6f3df5ec90c61195421ba72223de35ccfb2f88", size = 2676109 }, + { url = "https://files.pythonhosted.org/packages/f9/41/4b043778cf9c4285d59742281a769eac371b9e47e35f98ad321349cc5d61/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f57a69461af2a5fa6e6bbd7a5f60d3b7e6cebb687f55106933188e79ad155c1", size = 2002028 }, + { url = "https://files.pythonhosted.org/packages/cb/d5/7bb781bf2748ce3d03af04d5c969fa1308880e1dca35a9bd94e1a96a922e/pydantic_core-2.33.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:572c7e6c8bb4774d2ac88929e3d1f12bc45714ae5ee6d9a788a9fb35e60bb04b", size = 2100044 }, + { url = "https://files.pythonhosted.org/packages/fe/36/def5e53e1eb0ad896785702a5bbfd25eed546cdcf4087ad285021a90ed53/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:db4b41f9bd95fbe5acd76d89920336ba96f03e149097365afe1cb092fceb89a1", size = 2058881 }, + { url = "https://files.pythonhosted.org/packages/01/6c/57f8d70b2ee57fc3dc8b9610315949837fa8c11d86927b9bb044f8705419/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:fa854f5cf7e33842a892e5c73f45327760bc7bc516339fda888c75ae60edaeb6", size = 2227034 }, + { url = "https://files.pythonhosted.org/packages/27/b9/9c17f0396a82b3d5cbea4c24d742083422639e7bb1d5bf600e12cb176a13/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5f483cfb75ff703095c59e365360cb73e00185e01aaea067cd19acffd2ab20ea", size = 2234187 }, + { url = "https://files.pythonhosted.org/packages/b0/6a/adf5734ffd52bf86d865093ad70b2ce543415e0e356f6cacabbc0d9ad910/pydantic_core-2.33.2-cp312-cp312-win32.whl", hash = "sha256:9cb1da0f5a471435a7bc7e439b8a728e8b61e59784b2af70d7c169f8dd8ae290", size = 1892628 }, + { url = "https://files.pythonhosted.org/packages/43/e4/5479fecb3606c1368d496a825d8411e126133c41224c1e7238be58b87d7e/pydantic_core-2.33.2-cp312-cp312-win_amd64.whl", hash = "sha256:f941635f2a3d96b2973e867144fde513665c87f13fe0e193c158ac51bfaaa7b2", size = 1955866 }, + { url = "https://files.pythonhosted.org/packages/0d/24/8b11e8b3e2be9dd82df4b11408a67c61bb4dc4f8e11b5b0fc888b38118b5/pydantic_core-2.33.2-cp312-cp312-win_arm64.whl", hash = "sha256:cca3868ddfaccfbc4bfb1d608e2ccaaebe0ae628e1416aeb9c4d88c001bb45ab", size = 1888894 }, + { url = "https://files.pythonhosted.org/packages/46/8c/99040727b41f56616573a28771b1bfa08a3d3fe74d3d513f01251f79f172/pydantic_core-2.33.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f", size = 2015688 }, + { url = "https://files.pythonhosted.org/packages/3a/cc/5999d1eb705a6cefc31f0b4a90e9f7fc400539b1a1030529700cc1b51838/pydantic_core-2.33.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6", size = 1844808 }, + { url = "https://files.pythonhosted.org/packages/6f/5e/a0a7b8885c98889a18b6e376f344da1ef323d270b44edf8174d6bce4d622/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef", size = 1885580 }, + { url = "https://files.pythonhosted.org/packages/3b/2a/953581f343c7d11a304581156618c3f592435523dd9d79865903272c256a/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a", size = 1973859 }, + { url = "https://files.pythonhosted.org/packages/e6/55/f1a813904771c03a3f97f676c62cca0c0a4138654107c1b61f19c644868b/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916", size = 2120810 }, + { url = "https://files.pythonhosted.org/packages/aa/c3/053389835a996e18853ba107a63caae0b9deb4a276c6b472931ea9ae6e48/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a", size = 2676498 }, + { url = "https://files.pythonhosted.org/packages/eb/3c/f4abd740877a35abade05e437245b192f9d0ffb48bbbbd708df33d3cda37/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d", size = 2000611 }, + { url = "https://files.pythonhosted.org/packages/59/a7/63ef2fed1837d1121a894d0ce88439fe3e3b3e48c7543b2a4479eb99c2bd/pydantic_core-2.33.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56", size = 2107924 }, + { url = "https://files.pythonhosted.org/packages/04/8f/2551964ef045669801675f1cfc3b0d74147f4901c3ffa42be2ddb1f0efc4/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5", size = 2063196 }, + { url = "https://files.pythonhosted.org/packages/26/bd/d9602777e77fc6dbb0c7db9ad356e9a985825547dce5ad1d30ee04903918/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e", size = 2236389 }, + { url = "https://files.pythonhosted.org/packages/42/db/0e950daa7e2230423ab342ae918a794964b053bec24ba8af013fc7c94846/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162", size = 2239223 }, + { url = "https://files.pythonhosted.org/packages/58/4d/4f937099c545a8a17eb52cb67fe0447fd9a373b348ccfa9a87f141eeb00f/pydantic_core-2.33.2-cp313-cp313-win32.whl", hash = "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849", size = 1900473 }, + { url = "https://files.pythonhosted.org/packages/a0/75/4a0a9bac998d78d889def5e4ef2b065acba8cae8c93696906c3a91f310ca/pydantic_core-2.33.2-cp313-cp313-win_amd64.whl", hash = "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9", size = 1955269 }, + { url = "https://files.pythonhosted.org/packages/f9/86/1beda0576969592f1497b4ce8e7bc8cbdf614c352426271b1b10d5f0aa64/pydantic_core-2.33.2-cp313-cp313-win_arm64.whl", hash = "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9", size = 1893921 }, + { url = "https://files.pythonhosted.org/packages/a4/7d/e09391c2eebeab681df2b74bfe6c43422fffede8dc74187b2b0bf6fd7571/pydantic_core-2.33.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac", size = 1806162 }, + { url = "https://files.pythonhosted.org/packages/f1/3d/847b6b1fed9f8ed3bb95a9ad04fbd0b212e832d4f0f50ff4d9ee5a9f15cf/pydantic_core-2.33.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5", size = 1981560 }, + { url = "https://files.pythonhosted.org/packages/6f/9a/e73262f6c6656262b5fdd723ad90f518f579b7bc8622e43a942eec53c938/pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9", size = 1935777 }, + { url = "https://files.pythonhosted.org/packages/7b/27/d4ae6487d73948d6f20dddcd94be4ea43e74349b56eba82e9bdee2d7494c/pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:dd14041875d09cc0f9308e37a6f8b65f5585cf2598a53aa0123df8b129d481f8", size = 2025200 }, + { url = "https://files.pythonhosted.org/packages/f1/b8/b3cb95375f05d33801024079b9392a5ab45267a63400bf1866e7ce0f0de4/pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d87c561733f66531dced0da6e864f44ebf89a8fba55f31407b00c2f7f9449593", size = 1859123 }, + { url = "https://files.pythonhosted.org/packages/05/bc/0d0b5adeda59a261cd30a1235a445bf55c7e46ae44aea28f7bd6ed46e091/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f82865531efd18d6e07a04a17331af02cb7a651583c418df8266f17a63c6612", size = 1892852 }, + { url = "https://files.pythonhosted.org/packages/3e/11/d37bdebbda2e449cb3f519f6ce950927b56d62f0b84fd9cb9e372a26a3d5/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bfb5112df54209d820d7bf9317c7a6c9025ea52e49f46b6a2060104bba37de7", size = 2067484 }, + { url = "https://files.pythonhosted.org/packages/8c/55/1f95f0a05ce72ecb02a8a8a1c3be0579bbc29b1d5ab68f1378b7bebc5057/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:64632ff9d614e5eecfb495796ad51b0ed98c453e447a76bcbeeb69615079fc7e", size = 2108896 }, + { url = "https://files.pythonhosted.org/packages/53/89/2b2de6c81fa131f423246a9109d7b2a375e83968ad0800d6e57d0574629b/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:f889f7a40498cc077332c7ab6b4608d296d852182211787d4f3ee377aaae66e8", size = 2069475 }, + { url = "https://files.pythonhosted.org/packages/b8/e9/1f7efbe20d0b2b10f6718944b5d8ece9152390904f29a78e68d4e7961159/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:de4b83bb311557e439b9e186f733f6c645b9417c84e2eb8203f3f820a4b988bf", size = 2239013 }, + { url = "https://files.pythonhosted.org/packages/3c/b2/5309c905a93811524a49b4e031e9851a6b00ff0fb668794472ea7746b448/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:82f68293f055f51b51ea42fafc74b6aad03e70e191799430b90c13d643059ebb", size = 2238715 }, + { url = "https://files.pythonhosted.org/packages/32/56/8a7ca5d2cd2cda1d245d34b1c9a942920a718082ae8e54e5f3e5a58b7add/pydantic_core-2.33.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:329467cecfb529c925cf2bbd4d60d2c509bc2fb52a20c1045bf09bb70971a9c1", size = 2066757 }, +] + +[[package]] +name = "pygments" +version = "2.19.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7c/2d/c3338d48ea6cc0feb8446d8e6937e1408088a72a39937982cc6111d17f84/pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", size = 4968581 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293 }, +] + +[[package]] +name = "pyogrio" +version = "0.11.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "numpy" }, + { name = "packaging" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/54/c3/5e30f913ad8a975abe6f6582a2d3cf321bdf40fd696940d9283c63880c7a/pyogrio-0.11.0.tar.gz", hash = "sha256:a7e0a97bc10c0d7204f6bf52e1b928cba0554c35a907c32b23065aed1ed97b3f", size = 286915 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/d1/035667f23d8e7066471c500636e9ee77b159a9d92f32b5e4944d541aad69/pyogrio-0.11.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:862b79d36d39c1f755739bde00cfd82fd1034fd287084d9202b14e3a85576f5c", size = 19492247 }, + { url = "https://files.pythonhosted.org/packages/0b/da/558be674dbbf18b9cb2f31b8c9d5691e1a42100bdbd159b4771f608f01e2/pyogrio-0.11.0-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:21b1924c02513185e3df1301dfc9d313f1450d7c366f8629e26757f51ba31003", size = 20678449 }, + { url = "https://files.pythonhosted.org/packages/c4/78/3761a80818a148ba9544abaf9c41bef5353054054c5ed16872e65cbf9dd6/pyogrio-0.11.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:103313202414ffa7378016791d287442541af60ac57b78536f0c67f3a82904a4", size = 27068276 }, + { url = "https://files.pythonhosted.org/packages/ad/6c/9a6faa094b33054957b4eef389106aa4f94e9dbdd384c9db5f03d6a4d379/pyogrio-0.11.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:2e48956e68c41a17cbf3df32d979553de2839a082a7a9b0beef14948aa4ca5df", size = 26571289 }, + { url = "https://files.pythonhosted.org/packages/25/19/6a24c2052f2f99190482c83dcf8ecdc02bde9c8dbc2d604f088f9bbb5dbb/pyogrio-0.11.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:ec5666cc8bf97aef9993c998198f85fe209b8a9ad4737696d3d2ab573b3e9a5b", size = 27769581 }, + { url = "https://files.pythonhosted.org/packages/3d/ad/afc1cdea5dac6afb95d561c9ec73c27722d494d8faab7e0452cf71fba71f/pyogrio-0.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:8ad3744e679de2a31b1a885dc5ea260e3482f0d5e71461a88f431cda8d536b17", size = 19178064 }, + { url = "https://files.pythonhosted.org/packages/22/39/927036db0c550d35efb4d998dfe90c56515bc14d6ed0166b6c01ca28be24/pyogrio-0.11.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:a6f114d32c5c8a157c6fbf74e3ecfe69be7efb29363102f2aad14c9813de637a", size = 19491944 }, + { url = "https://files.pythonhosted.org/packages/49/78/92db6ca3650996ca80287e59b799aa303ccecd4f1cd677f15832e466d9e2/pyogrio-0.11.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:596e3f26e792882e35f25715634c12c1d6658a3d8d178c0089a9462c56b48be5", size = 20674571 }, + { url = "https://files.pythonhosted.org/packages/c8/a4/bc37ddcee3f47c79197887d6386d31d97182a94cff6a5093cad37d873bc5/pyogrio-0.11.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11d693ca24e80bd7ede7b27ea3598593be5b41fb7cec315a57f5bb24d15faef8", size = 27033355 }, + { url = "https://files.pythonhosted.org/packages/5c/6f/984a513d5deab8ca94dde440084cab3eda5684825d70395a3bd21c2a9e5d/pyogrio-0.11.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:961100786ae44e2f27b4049b5262e378a3cba07872fc22051905fed8b4ce42db", size = 26528521 }, + { url = "https://files.pythonhosted.org/packages/39/d6/6026ef8903aef2a15b7ba5ad84c74ca2ce67d29fc6d99e07262a65061619/pyogrio-0.11.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:334563d24defc5d706bd2a1fa7d7433e33140e64b0fb9cb4afc715e4f6035c2b", size = 27734210 }, + { url = "https://files.pythonhosted.org/packages/94/81/232d4808e54e026b9059f966bc2a4a5de7e42f42e4bd4e3897e1b31ea87f/pyogrio-0.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:bf1f9128136abcbd1605d6fc6bf8c529c2092558246d8046ee6fbc383c550074", size = 19165401 }, + { url = "https://files.pythonhosted.org/packages/ba/2b/098692d9be9defb5d40327af50ffdc0c5486a4724c06b3d1f757cd5abd6d/pyogrio-0.11.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:0b39e34199460dcd6a606db184094e69bcba89d1babb9a76cee74a134b53b232", size = 19485661 }, + { url = "https://files.pythonhosted.org/packages/00/06/5c197d76ea33d4667f427309b108281e7a3a0224e9a32c3fdb3c54e47133/pyogrio-0.11.0-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:5a952ef7a68fdfaf796a91b88c706108cb50ddd0a74096418e84aab7ac8a38be", size = 20667327 }, + { url = "https://files.pythonhosted.org/packages/9d/24/08715971846562624e1f185fc6f93d0a305950cc9167ac0b761f571c3c62/pyogrio-0.11.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4527abcac23bdac5781f9be9a7dd55fccd9967c7241a8e53de8ea1a06ea0cc2b", size = 27007054 }, + { url = "https://files.pythonhosted.org/packages/0d/07/c6c6d33e5b052b6bb785904477e906ed880509bc3748862ef59ed017739a/pyogrio-0.11.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:373a29d56a9016978aff57b88a640b5a8c3024dba7be1c059ad5af4ba932b59e", size = 26493010 }, + { url = "https://files.pythonhosted.org/packages/9f/bb/e12bebcf2668bcb83736cc76177f36ee300ac8069880fca3a73f8753fc70/pyogrio-0.11.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:ea2a131369ae8e62e30fa4f7e1442074d4828417d05ded660acea04a6a1d199b", size = 27710440 }, + { url = "https://files.pythonhosted.org/packages/46/8f/a9d134fbbf213db259b79f5bd5bbe7e3de1ff34fbe2a0b0be9d7d2919323/pyogrio-0.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:bf041d65bd1e89a4bb61845579c2963f2cca1bb33cde79f4ec2c0e0dc6f93afb", size = 19163300 }, +] + +[[package]] +name = "pyparsing" +version = "3.2.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bb/22/f1129e69d94ffff626bdb5c835506b3a5b4f3d070f17ea295e12c2c6f60f/pyparsing-3.2.3.tar.gz", hash = "sha256:b9c13f1ab8b3b542f72e28f634bad4de758ab3ce4546e4301970ad6fa77c38be", size = 1088608 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/05/e7/df2285f3d08fee213f2d041540fa4fc9ca6c2d44cf36d3a035bf2a8d2bcc/pyparsing-3.2.3-py3-none-any.whl", hash = "sha256:a749938e02d6fd0b59b356ca504a24982314bb090c383e3cf201c95ef7e2bfcf", size = 111120 }, +] + +[[package]] +name = "pyproj" +version = "3.7.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/67/10/a8480ea27ea4bbe896c168808854d00f2a9b49f95c0319ddcbba693c8a90/pyproj-3.7.1.tar.gz", hash = "sha256:60d72facd7b6b79853f19744779abcd3f804c4e0d4fa8815469db20c9f640a47", size = 226339 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ab/0d/63670fc527e664068b70b7cab599aa38b7420dd009bdc29ea257e7f3dfb3/pyproj-3.7.1-cp311-cp311-macosx_13_0_x86_64.whl", hash = "sha256:a94e26c1a4950cea40116775588a2ca7cf56f1f434ff54ee35a84718f3841a3d", size = 6264315 }, + { url = "https://files.pythonhosted.org/packages/25/9d/cbaf82cfb290d1f1fa42feb9ba9464013bb3891e40c4199f8072112e4589/pyproj-3.7.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:263b54ba5004b6b957d55757d846fc5081bc02980caa0279c4fc95fa0fff6067", size = 4666267 }, + { url = "https://files.pythonhosted.org/packages/79/53/24f9f9b8918c0550f3ff49ad5de4cf3f0688c9f91ff191476db8979146fe/pyproj-3.7.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f6d6a2ccd5607cd15ef990c51e6f2dd27ec0a741e72069c387088bba3aab60fa", size = 9680510 }, + { url = "https://files.pythonhosted.org/packages/3c/ac/12fab74a908d40b63174dc704587febd0729414804bbfd873cabe504ff2d/pyproj-3.7.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c5dcf24ede53d8abab7d8a77f69ff1936c6a8843ef4fcc574646e4be66e5739", size = 9493619 }, + { url = "https://files.pythonhosted.org/packages/c4/45/26311d6437135da2153a178125db5dfb6abce831ce04d10ec207eabac70a/pyproj-3.7.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3c2e7449840a44ce860d8bea2c6c1c4bc63fa07cba801dcce581d14dcb031a02", size = 10709755 }, + { url = "https://files.pythonhosted.org/packages/99/52/4ecd0986f27d0e6c8ee3a7bc5c63da15acd30ac23034f871325b297e61fd/pyproj-3.7.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0829865c1d3a3543f918b3919dc601eea572d6091c0dd175e1a054db9c109274", size = 10642970 }, + { url = "https://files.pythonhosted.org/packages/3f/a5/d3bfc018fc92195a000d1d28acc1f3f1df15ff9f09ece68f45a2636c0134/pyproj-3.7.1-cp311-cp311-win32.whl", hash = "sha256:6181960b4b812e82e588407fe5c9c68ada267c3b084db078f248db5d7f45d18a", size = 5868295 }, + { url = "https://files.pythonhosted.org/packages/92/39/ef6f06a5b223dbea308cfcbb7a0f72e7b506aef1850e061b2c73b0818715/pyproj-3.7.1-cp311-cp311-win_amd64.whl", hash = "sha256:5ad0ff443a785d84e2b380869fdd82e6bfc11eba6057d25b4409a9bbfa867970", size = 6279871 }, + { url = "https://files.pythonhosted.org/packages/e6/c9/876d4345b8d17f37ac59ebd39f8fa52fc6a6a9891a420f72d050edb6b899/pyproj-3.7.1-cp312-cp312-macosx_13_0_x86_64.whl", hash = "sha256:2781029d90df7f8d431e29562a3f2d8eafdf233c4010d6fc0381858dc7373217", size = 6264087 }, + { url = "https://files.pythonhosted.org/packages/ff/e6/5f8691f8c90e7f402cc80a6276eb19d2ec1faa150d5ae2dd9c7b0a254da8/pyproj-3.7.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:d61bf8ab04c73c1da08eedaf21a103b72fa5b0a9b854762905f65ff8b375d394", size = 4669628 }, + { url = "https://files.pythonhosted.org/packages/42/ec/16475bbb79c1c68845c0a0d9c60c4fb31e61b8a2a20bc18b1a81e81c7f68/pyproj-3.7.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:04abc517a8555d1b05fcee768db3280143fe42ec39fdd926a2feef31631a1f2f", size = 9721415 }, + { url = "https://files.pythonhosted.org/packages/b3/a3/448f05b15e318bd6bea9a32cfaf11e886c4ae61fa3eee6e09ed5c3b74bb2/pyproj-3.7.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:084c0a475688f934d386c2ab3b6ce03398a473cd48adfda70d9ab8f87f2394a0", size = 9556447 }, + { url = "https://files.pythonhosted.org/packages/6a/ae/bd15fe8d8bd914ead6d60bca7f895a4e6f8ef7e3928295134ff9a7dad14c/pyproj-3.7.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a20727a23b1e49c7dc7fe3c3df8e56a8a7acdade80ac2f5cca29d7ca5564c145", size = 10758317 }, + { url = "https://files.pythonhosted.org/packages/9d/d9/5ccefb8bca925f44256b188a91c31238cae29ab6ee7f53661ecc04616146/pyproj-3.7.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:bf84d766646f1ebd706d883755df4370aaf02b48187cedaa7e4239f16bc8213d", size = 10771259 }, + { url = "https://files.pythonhosted.org/packages/2a/7d/31dedff9c35fa703162f922eeb0baa6c44a3288469a5fd88d209e2892f9e/pyproj-3.7.1-cp312-cp312-win32.whl", hash = "sha256:5f0da2711364d7cb9f115b52289d4a9b61e8bca0da57f44a3a9d6fc9bdeb7274", size = 5859914 }, + { url = "https://files.pythonhosted.org/packages/3e/47/c6ab03d6564a7c937590cff81a2742b5990f096cce7c1a622d325be340ee/pyproj-3.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:aee664a9d806612af30a19dba49e55a7a78ebfec3e9d198f6a6176e1d140ec98", size = 6273196 }, + { url = "https://files.pythonhosted.org/packages/ef/01/984828464c9960036c602753fc0f21f24f0aa9043c18fa3f2f2b66a86340/pyproj-3.7.1-cp313-cp313-macosx_13_0_x86_64.whl", hash = "sha256:5f8d02ef4431dee414d1753d13fa82a21a2f61494737b5f642ea668d76164d6d", size = 6253062 }, + { url = "https://files.pythonhosted.org/packages/68/65/6ecdcdc829811a2c160cdfe2f068a009fc572fd4349664f758ccb0853a7c/pyproj-3.7.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:0b853ae99bda66cbe24b4ccfe26d70601d84375940a47f553413d9df570065e0", size = 4660548 }, + { url = "https://files.pythonhosted.org/packages/67/da/dda94c4490803679230ba4c17a12f151b307a0d58e8110820405ca2d98db/pyproj-3.7.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:83db380c52087f9e9bdd8a527943b2e7324f275881125e39475c4f9277bdeec4", size = 9662464 }, + { url = "https://files.pythonhosted.org/packages/6f/57/f61b7d22c91ae1d12ee00ac4c0038714e774ebcd851b9133e5f4f930dd40/pyproj-3.7.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b35ed213892e211a3ce2bea002aa1183e1a2a9b79e51bb3c6b15549a831ae528", size = 9497461 }, + { url = "https://files.pythonhosted.org/packages/b7/f6/932128236f79d2ac7d39fe1a19667fdf7155d9a81d31fb9472a7a497790f/pyproj-3.7.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a8b15b0463d1303bab113d1a6af2860a0d79013c3a66fcc5475ce26ef717fd4f", size = 10708869 }, + { url = "https://files.pythonhosted.org/packages/1d/0d/07ac7712994454a254c383c0d08aff9916a2851e6512d59da8dc369b1b02/pyproj-3.7.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:87229e42b75e89f4dad6459200f92988c5998dfb093c7c631fb48524c86cd5dc", size = 10729260 }, + { url = "https://files.pythonhosted.org/packages/b0/d0/9c604bc72c37ba69b867b6df724d6a5af6789e8c375022c952f65b2af558/pyproj-3.7.1-cp313-cp313-win32.whl", hash = "sha256:d666c3a3faaf3b1d7fc4a544059c4eab9d06f84a604b070b7aa2f318e227798e", size = 5855462 }, + { url = "https://files.pythonhosted.org/packages/98/df/68a2b7f5fb6400c64aad82d72bcc4bc531775e62eedff993a77c780defd0/pyproj-3.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:d3caac7473be22b6d6e102dde6c46de73b96bc98334e577dfaee9886f102ea2e", size = 6266573 }, +] + +[[package]] +name = "pyright" +version = "1.1.401" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nodeenv" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/79/9a/7ab2b333b921b2d6bfcffe05a0e0a0bbeff884bd6fb5ed50cd68e2898e53/pyright-1.1.401.tar.gz", hash = "sha256:788a82b6611fa5e34a326a921d86d898768cddf59edde8e93e56087d277cc6f1", size = 3894193 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0d/e6/1f908fce68b0401d41580e0f9acc4c3d1b248adcff00dfaad75cd21a1370/pyright-1.1.401-py3-none-any.whl", hash = "sha256:6fde30492ba5b0d7667c16ecaf6c699fab8d7a1263f6a18549e0b00bf7724c06", size = 5629193 }, +] + +[[package]] +name = "pysocks" +version = "1.7.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bd/11/293dd436aea955d45fc4e8a35b6ae7270f5b8e00b53cf6c024c83b657a11/PySocks-1.7.1.tar.gz", hash = "sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0", size = 284429 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8d/59/b4572118e098ac8e46e399a1dd0f2d85403ce8bbaad9ec79373ed6badaf9/PySocks-1.7.1-py3-none-any.whl", hash = "sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5", size = 16725 }, +] + +[[package]] +name = "pytest" +version = "8.3.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ae/3c/c9d525a414d506893f0cd8a8d0de7706446213181570cdbd766691164e40/pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845", size = 1450891 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/30/3d/64ad57c803f1fa1e963a7946b6e0fea4a70df53c1a7fed304586539c2bac/pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820", size = 343634 }, +] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892 }, +] + +[[package]] +name = "python-dotenv" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/88/2c/7bb1416c5620485aa793f2de31d3df393d3686aa8a8506d11e10e13c5baf/python_dotenv-1.1.0.tar.gz", hash = "sha256:41f90bc6f5f177fb41f53e87666db362025010eb28f60a01c9143bfa33a2b2d5", size = 39920 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/18/98a99ad95133c6a6e2005fe89faedf294a748bd5dc803008059409ac9b1e/python_dotenv-1.1.0-py3-none-any.whl", hash = "sha256:d7c01d9e2293916c18baf562d95698754b0dbbb5e74d457c45d4f6561fb9d55d", size = 20256 }, +] + +[[package]] +name = "pytorch-lightning" +version = "2.5.1.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "fsspec", extra = ["http"] }, + { name = "lightning-utilities" }, + { name = "packaging" }, + { name = "pyyaml" }, + { name = "torch" }, + { name = "torchmetrics" }, + { name = "tqdm" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fe/0c/cfa6223c525f67ea3a7a2907e36e9e9a9653300f82cfd9af88f8136514ab/pytorch_lightning-2.5.1.post0.tar.gz", hash = "sha256:abc3d5a804d41f941b14e3fd7db5572a1270cd1e9889b50e962984c87d498d94", size = 634368 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/a9/e14821cfaf08e8d78185cca0477c9d3a62bafe1b4b530100f7b66bb1f7bb/pytorch_lightning-2.5.1.post0-py3-none-any.whl", hash = "sha256:873fb21392c8b79908218f5ca8f65bd835439216e52550c36ff55d849e99c93e", size = 823084 }, +] + +[[package]] +name = "pytz" +version = "2025.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f8/bf/abbd3cdfb8fbc7fb3d4d38d320f2441b1e7cbe29be4f23797b4a2b5d8aac/pytz-2025.2.tar.gz", hash = "sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3", size = 320884 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.py3-none-any.whl", hash = "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00", size = 509225 }, +] + +[[package]] +name = "pywin32" +version = "310" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f7/b1/68aa2986129fb1011dabbe95f0136f44509afaf072b12b8f815905a39f33/pywin32-310-cp311-cp311-win32.whl", hash = "sha256:1e765f9564e83011a63321bb9d27ec456a0ed90d3732c4b2e312b855365ed8bd", size = 8784284 }, + { url = "https://files.pythonhosted.org/packages/b3/bd/d1592635992dd8db5bb8ace0551bc3a769de1ac8850200cfa517e72739fb/pywin32-310-cp311-cp311-win_amd64.whl", hash = "sha256:126298077a9d7c95c53823934f000599f66ec9296b09167810eb24875f32689c", size = 9520748 }, + { url = "https://files.pythonhosted.org/packages/90/b1/ac8b1ffce6603849eb45a91cf126c0fa5431f186c2e768bf56889c46f51c/pywin32-310-cp311-cp311-win_arm64.whl", hash = "sha256:19ec5fc9b1d51c4350be7bb00760ffce46e6c95eaf2f0b2f1150657b1a43c582", size = 8455941 }, + { url = "https://files.pythonhosted.org/packages/6b/ec/4fdbe47932f671d6e348474ea35ed94227fb5df56a7c30cbbb42cd396ed0/pywin32-310-cp312-cp312-win32.whl", hash = "sha256:8a75a5cc3893e83a108c05d82198880704c44bbaee4d06e442e471d3c9ea4f3d", size = 8796239 }, + { url = "https://files.pythonhosted.org/packages/e3/e5/b0627f8bb84e06991bea89ad8153a9e50ace40b2e1195d68e9dff6b03d0f/pywin32-310-cp312-cp312-win_amd64.whl", hash = "sha256:bf5c397c9a9a19a6f62f3fb821fbf36cac08f03770056711f765ec1503972060", size = 9503839 }, + { url = "https://files.pythonhosted.org/packages/1f/32/9ccf53748df72301a89713936645a664ec001abd35ecc8578beda593d37d/pywin32-310-cp312-cp312-win_arm64.whl", hash = "sha256:2349cc906eae872d0663d4d6290d13b90621eaf78964bb1578632ff20e152966", size = 8459470 }, + { url = "https://files.pythonhosted.org/packages/1c/09/9c1b978ffc4ae53999e89c19c77ba882d9fce476729f23ef55211ea1c034/pywin32-310-cp313-cp313-win32.whl", hash = "sha256:5d241a659c496ada3253cd01cfaa779b048e90ce4b2b38cd44168ad555ce74ab", size = 8794384 }, + { url = "https://files.pythonhosted.org/packages/45/3c/b4640f740ffebadd5d34df35fecba0e1cfef8fde9f3e594df91c28ad9b50/pywin32-310-cp313-cp313-win_amd64.whl", hash = "sha256:667827eb3a90208ddbdcc9e860c81bde63a135710e21e4cb3348968e4bd5249e", size = 9503039 }, + { url = "https://files.pythonhosted.org/packages/b4/f4/f785020090fb050e7fb6d34b780f2231f302609dc964672f72bfaeb59a28/pywin32-310-cp313-cp313-win_arm64.whl", hash = "sha256:e308f831de771482b7cf692a1f308f8fca701b2d8f9dde6cc440c7da17e47b33", size = 8458152 }, +] + +[[package]] +name = "pyyaml" +version = "6.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f8/aa/7af4e81f7acba21a4c6be026da38fd2b872ca46226673c89a758ebdc4fd2/PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", size = 184612 }, + { url = "https://files.pythonhosted.org/packages/8b/62/b9faa998fd185f65c1371643678e4d58254add437edb764a08c5a98fb986/PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", size = 172040 }, + { url = "https://files.pythonhosted.org/packages/ad/0c/c804f5f922a9a6563bab712d8dcc70251e8af811fce4524d57c2c0fd49a4/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", size = 736829 }, + { url = "https://files.pythonhosted.org/packages/51/16/6af8d6a6b210c8e54f1406a6b9481febf9c64a3109c541567e35a49aa2e7/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", size = 764167 }, + { url = "https://files.pythonhosted.org/packages/75/e4/2c27590dfc9992f73aabbeb9241ae20220bd9452df27483b6e56d3975cc5/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", size = 762952 }, + { url = "https://files.pythonhosted.org/packages/9b/97/ecc1abf4a823f5ac61941a9c00fe501b02ac3ab0e373c3857f7d4b83e2b6/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4", size = 735301 }, + { url = "https://files.pythonhosted.org/packages/45/73/0f49dacd6e82c9430e46f4a027baa4ca205e8b0a9dce1397f44edc23559d/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", size = 756638 }, + { url = "https://files.pythonhosted.org/packages/22/5f/956f0f9fc65223a58fbc14459bf34b4cc48dec52e00535c79b8db361aabd/PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", size = 143850 }, + { url = "https://files.pythonhosted.org/packages/ed/23/8da0bbe2ab9dcdd11f4f4557ccaf95c10b9811b13ecced089d43ce59c3c8/PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", size = 161980 }, + { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873 }, + { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302 }, + { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154 }, + { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223 }, + { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542 }, + { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164 }, + { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611 }, + { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591 }, + { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338 }, + { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309 }, + { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679 }, + { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428 }, + { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361 }, + { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523 }, + { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660 }, + { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597 }, + { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527 }, + { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446 }, +] + +[[package]] +name = "pyzmq" +version = "26.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi", marker = "implementation_name == 'pypy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/11/b9213d25230ac18a71b39b3723494e57adebe36e066397b961657b3b41c1/pyzmq-26.4.0.tar.gz", hash = "sha256:4bd13f85f80962f91a651a7356fe0472791a5f7a92f227822b5acf44795c626d", size = 278293 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/32/6d/234e3b0aa82fd0290b1896e9992f56bdddf1f97266110be54d0177a9d2d9/pyzmq-26.4.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:bfcf82644c9b45ddd7cd2a041f3ff8dce4a0904429b74d73a439e8cab1bd9e54", size = 1339723 }, + { url = "https://files.pythonhosted.org/packages/4f/11/6d561efe29ad83f7149a7cd48e498e539ed09019c6cd7ecc73f4cc725028/pyzmq-26.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9bcae3979b2654d5289d3490742378b2f3ce804b0b5fd42036074e2bf35b030", size = 672645 }, + { url = "https://files.pythonhosted.org/packages/19/fd/81bfe3e23f418644660bad1a90f0d22f0b3eebe33dd65a79385530bceb3d/pyzmq-26.4.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ccdff8ac4246b6fb60dcf3982dfaeeff5dd04f36051fe0632748fc0aa0679c01", size = 910133 }, + { url = "https://files.pythonhosted.org/packages/97/68/321b9c775595ea3df832a9516252b653fe32818db66fdc8fa31c9b9fce37/pyzmq-26.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4550af385b442dc2d55ab7717837812799d3674cb12f9a3aa897611839c18e9e", size = 867428 }, + { url = "https://files.pythonhosted.org/packages/4e/6e/159cbf2055ef36aa2aa297e01b24523176e5b48ead283c23a94179fb2ba2/pyzmq-26.4.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:2f9f7ffe9db1187a253fca95191854b3fda24696f086e8789d1d449308a34b88", size = 862409 }, + { url = "https://files.pythonhosted.org/packages/05/1c/45fb8db7be5a7d0cadea1070a9cbded5199a2d578de2208197e592f219bd/pyzmq-26.4.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:3709c9ff7ba61589b7372923fd82b99a81932b592a5c7f1a24147c91da9a68d6", size = 1205007 }, + { url = "https://files.pythonhosted.org/packages/f8/fa/658c7f583af6498b463f2fa600f34e298e1b330886f82f1feba0dc2dd6c3/pyzmq-26.4.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:f8f3c30fb2d26ae5ce36b59768ba60fb72507ea9efc72f8f69fa088450cff1df", size = 1514599 }, + { url = "https://files.pythonhosted.org/packages/4d/d7/44d641522353ce0a2bbd150379cb5ec32f7120944e6bfba4846586945658/pyzmq-26.4.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:382a4a48c8080e273427fc692037e3f7d2851959ffe40864f2db32646eeb3cef", size = 1414546 }, + { url = "https://files.pythonhosted.org/packages/72/76/c8ed7263218b3d1e9bce07b9058502024188bd52cc0b0a267a9513b431fc/pyzmq-26.4.0-cp311-cp311-win32.whl", hash = "sha256:d56aad0517d4c09e3b4f15adebba8f6372c5102c27742a5bdbfc74a7dceb8fca", size = 579247 }, + { url = "https://files.pythonhosted.org/packages/c3/d0/2d9abfa2571a0b1a67c0ada79a8aa1ba1cce57992d80f771abcdf99bb32c/pyzmq-26.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:963977ac8baed7058c1e126014f3fe58b3773f45c78cce7af5c26c09b6823896", size = 644727 }, + { url = "https://files.pythonhosted.org/packages/0d/d1/c8ad82393be6ccedfc3c9f3adb07f8f3976e3c4802640fe3f71441941e70/pyzmq-26.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:c0c8e8cadc81e44cc5088fcd53b9b3b4ce9344815f6c4a03aec653509296fae3", size = 559942 }, + { url = "https://files.pythonhosted.org/packages/10/44/a778555ebfdf6c7fc00816aad12d185d10a74d975800341b1bc36bad1187/pyzmq-26.4.0-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:5227cb8da4b6f68acfd48d20c588197fd67745c278827d5238c707daf579227b", size = 1341586 }, + { url = "https://files.pythonhosted.org/packages/9c/4f/f3a58dc69ac757e5103be3bd41fb78721a5e17da7cc617ddb56d973a365c/pyzmq-26.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1c07a7fa7f7ba86554a2b1bef198c9fed570c08ee062fd2fd6a4dcacd45f905", size = 665880 }, + { url = "https://files.pythonhosted.org/packages/fe/45/50230bcfb3ae5cb98bee683b6edeba1919f2565d7cc1851d3c38e2260795/pyzmq-26.4.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae775fa83f52f52de73183f7ef5395186f7105d5ed65b1ae65ba27cb1260de2b", size = 902216 }, + { url = "https://files.pythonhosted.org/packages/41/59/56bbdc5689be5e13727491ad2ba5efd7cd564365750514f9bc8f212eef82/pyzmq-26.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66c760d0226ebd52f1e6b644a9e839b5db1e107a23f2fcd46ec0569a4fdd4e63", size = 859814 }, + { url = "https://files.pythonhosted.org/packages/81/b1/57db58cfc8af592ce94f40649bd1804369c05b2190e4cbc0a2dad572baeb/pyzmq-26.4.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:ef8c6ecc1d520debc147173eaa3765d53f06cd8dbe7bd377064cdbc53ab456f5", size = 855889 }, + { url = "https://files.pythonhosted.org/packages/e8/92/47542e629cbac8f221c230a6d0f38dd3d9cff9f6f589ed45fdf572ffd726/pyzmq-26.4.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3150ef4084e163dec29ae667b10d96aad309b668fac6810c9e8c27cf543d6e0b", size = 1197153 }, + { url = "https://files.pythonhosted.org/packages/07/e5/b10a979d1d565d54410afc87499b16c96b4a181af46e7645ab4831b1088c/pyzmq-26.4.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:4448c9e55bf8329fa1dcedd32f661bf611214fa70c8e02fee4347bc589d39a84", size = 1507352 }, + { url = "https://files.pythonhosted.org/packages/ab/58/5a23db84507ab9c01c04b1232a7a763be66e992aa2e66498521bbbc72a71/pyzmq-26.4.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e07dde3647afb084d985310d067a3efa6efad0621ee10826f2cb2f9a31b89d2f", size = 1406834 }, + { url = "https://files.pythonhosted.org/packages/22/74/aaa837b331580c13b79ac39396601fb361454ee184ca85e8861914769b99/pyzmq-26.4.0-cp312-cp312-win32.whl", hash = "sha256:ba034a32ecf9af72adfa5ee383ad0fd4f4e38cdb62b13624278ef768fe5b5b44", size = 577992 }, + { url = "https://files.pythonhosted.org/packages/30/0f/55f8c02c182856743b82dde46b2dc3e314edda7f1098c12a8227eeda0833/pyzmq-26.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:056a97aab4064f526ecb32f4343917a4022a5d9efb6b9df990ff72e1879e40be", size = 640466 }, + { url = "https://files.pythonhosted.org/packages/e4/29/073779afc3ef6f830b8de95026ef20b2d1ec22d0324d767748d806e57379/pyzmq-26.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:2f23c750e485ce1eb639dbd576d27d168595908aa2d60b149e2d9e34c9df40e0", size = 556342 }, + { url = "https://files.pythonhosted.org/packages/d7/20/fb2c92542488db70f833b92893769a569458311a76474bda89dc4264bd18/pyzmq-26.4.0-cp313-cp313-macosx_10_15_universal2.whl", hash = "sha256:c43fac689880f5174d6fc864857d1247fe5cfa22b09ed058a344ca92bf5301e3", size = 1339484 }, + { url = "https://files.pythonhosted.org/packages/58/29/2f06b9cabda3a6ea2c10f43e67ded3e47fc25c54822e2506dfb8325155d4/pyzmq-26.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:902aca7eba477657c5fb81c808318460328758e8367ecdd1964b6330c73cae43", size = 666106 }, + { url = "https://files.pythonhosted.org/packages/77/e4/dcf62bd29e5e190bd21bfccaa4f3386e01bf40d948c239239c2f1e726729/pyzmq-26.4.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e5e48a830bfd152fe17fbdeaf99ac5271aa4122521bf0d275b6b24e52ef35eb6", size = 902056 }, + { url = "https://files.pythonhosted.org/packages/1a/cf/b36b3d7aea236087d20189bec1a87eeb2b66009731d7055e5c65f845cdba/pyzmq-26.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31be2b6de98c824c06f5574331f805707c667dc8f60cb18580b7de078479891e", size = 860148 }, + { url = "https://files.pythonhosted.org/packages/18/a6/f048826bc87528c208e90604c3bf573801e54bd91e390cbd2dfa860e82dc/pyzmq-26.4.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:6332452034be001bbf3206ac59c0d2a7713de5f25bb38b06519fc6967b7cf771", size = 855983 }, + { url = "https://files.pythonhosted.org/packages/0a/27/454d34ab6a1d9772a36add22f17f6b85baf7c16e14325fa29e7202ca8ee8/pyzmq-26.4.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:da8c0f5dd352136853e6a09b1b986ee5278dfddfebd30515e16eae425c872b30", size = 1197274 }, + { url = "https://files.pythonhosted.org/packages/f4/3d/7abfeab6b83ad38aa34cbd57c6fc29752c391e3954fd12848bd8d2ec0df6/pyzmq-26.4.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:f4ccc1a0a2c9806dda2a2dd118a3b7b681e448f3bb354056cad44a65169f6d86", size = 1507120 }, + { url = "https://files.pythonhosted.org/packages/13/ff/bc8d21dbb9bc8705126e875438a1969c4f77e03fc8565d6901c7933a3d01/pyzmq-26.4.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:1c0b5fceadbab461578daf8d1dcc918ebe7ddd2952f748cf30c7cf2de5d51101", size = 1406738 }, + { url = "https://files.pythonhosted.org/packages/f5/5d/d4cd85b24de71d84d81229e3bbb13392b2698432cf8fdcea5afda253d587/pyzmq-26.4.0-cp313-cp313-win32.whl", hash = "sha256:28e2b0ff5ba4b3dd11062d905682bad33385cfa3cc03e81abd7f0822263e6637", size = 577826 }, + { url = "https://files.pythonhosted.org/packages/c6/6c/f289c1789d7bb6e5a3b3bef7b2a55089b8561d17132be7d960d3ff33b14e/pyzmq-26.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:23ecc9d241004c10e8b4f49d12ac064cd7000e1643343944a10df98e57bc544b", size = 640406 }, + { url = "https://files.pythonhosted.org/packages/b3/99/676b8851cb955eb5236a0c1e9ec679ea5ede092bf8bf2c8a68d7e965cac3/pyzmq-26.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:1edb0385c7f025045d6e0f759d4d3afe43c17a3d898914ec6582e6f464203c08", size = 556216 }, + { url = "https://files.pythonhosted.org/packages/65/c2/1fac340de9d7df71efc59d9c50fc7a635a77b103392d1842898dd023afcb/pyzmq-26.4.0-cp313-cp313t-macosx_10_15_universal2.whl", hash = "sha256:93a29e882b2ba1db86ba5dd5e88e18e0ac6b627026c5cfbec9983422011b82d4", size = 1333769 }, + { url = "https://files.pythonhosted.org/packages/5c/c7/6c03637e8d742c3b00bec4f5e4cd9d1c01b2f3694c6f140742e93ca637ed/pyzmq-26.4.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb45684f276f57110bb89e4300c00f1233ca631f08f5f42528a5c408a79efc4a", size = 658826 }, + { url = "https://files.pythonhosted.org/packages/a5/97/a8dca65913c0f78e0545af2bb5078aebfc142ca7d91cdaffa1fbc73e5dbd/pyzmq-26.4.0-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f72073e75260cb301aad4258ad6150fa7f57c719b3f498cb91e31df16784d89b", size = 891650 }, + { url = "https://files.pythonhosted.org/packages/7d/7e/f63af1031eb060bf02d033732b910fe48548dcfdbe9c785e9f74a6cc6ae4/pyzmq-26.4.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be37e24b13026cfedd233bcbbccd8c0bcd2fdd186216094d095f60076201538d", size = 849776 }, + { url = "https://files.pythonhosted.org/packages/f6/fa/1a009ce582802a895c0d5fe9413f029c940a0a8ee828657a3bb0acffd88b/pyzmq-26.4.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:237b283044934d26f1eeff4075f751b05d2f3ed42a257fc44386d00df6a270cf", size = 842516 }, + { url = "https://files.pythonhosted.org/packages/6e/bc/f88b0bad0f7a7f500547d71e99f10336f2314e525d4ebf576a1ea4a1d903/pyzmq-26.4.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:b30f862f6768b17040929a68432c8a8be77780317f45a353cb17e423127d250c", size = 1189183 }, + { url = "https://files.pythonhosted.org/packages/d9/8c/db446a3dd9cf894406dec2e61eeffaa3c07c3abb783deaebb9812c4af6a5/pyzmq-26.4.0-cp313-cp313t-musllinux_1_1_i686.whl", hash = "sha256:c80fcd3504232f13617c6ab501124d373e4895424e65de8b72042333316f64a8", size = 1495501 }, + { url = "https://files.pythonhosted.org/packages/05/4c/bf3cad0d64c3214ac881299c4562b815f05d503bccc513e3fd4fdc6f67e4/pyzmq-26.4.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:26a2a7451606b87f67cdeca2c2789d86f605da08b4bd616b1a9981605ca3a364", size = 1395540 }, + { url = "https://files.pythonhosted.org/packages/04/52/a70fcd5592715702248306d8e1729c10742c2eac44529984413b05c68658/pyzmq-26.4.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:4478b14cb54a805088299c25a79f27eaf530564a7a4f72bf432a040042b554eb", size = 834405 }, + { url = "https://files.pythonhosted.org/packages/25/f9/1a03f1accff16b3af1a6fa22cbf7ced074776abbf688b2e9cb4629700c62/pyzmq-26.4.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a28ac29c60e4ba84b5f58605ace8ad495414a724fe7aceb7cf06cd0598d04e1", size = 569578 }, + { url = "https://files.pythonhosted.org/packages/76/0c/3a633acd762aa6655fcb71fa841907eae0ab1e8582ff494b137266de341d/pyzmq-26.4.0-pp311-pypy311_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:43b03c1ceea27c6520124f4fb2ba9c647409b9abdf9a62388117148a90419494", size = 798248 }, + { url = "https://files.pythonhosted.org/packages/cd/cc/6c99c84aa60ac1cc56747bed6be8ce6305b9b861d7475772e7a25ce019d3/pyzmq-26.4.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7731abd23a782851426d4e37deb2057bf9410848a4459b5ede4fe89342e687a9", size = 756757 }, + { url = "https://files.pythonhosted.org/packages/13/9c/d8073bd898eb896e94c679abe82e47506e2b750eb261cf6010ced869797c/pyzmq-26.4.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:a222ad02fbe80166b0526c038776e8042cd4e5f0dec1489a006a1df47e9040e0", size = 555371 }, +] + +[[package]] +name = "qdldl" +version = "0.1.7.post5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, + { name = "scipy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/59/88/9254835c513a381b8c6d52773060844acf76dfa739648c18f61809c8ee04/qdldl-0.1.7.post5.tar.gz", hash = "sha256:0b1399e1c49b5bed5aac8fd63ef08ab708d340c37fb426fe00128bc1f36b286e", size = 73920 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/15/6c/ce4cab36da9a7c0bff69067377b513ec88ff753de07f33f65959f4141308/qdldl-0.1.7.post5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:aa22df45e625c763d129b2893b284b7bde16a535a7e900288d588be9dc24fe9f", size = 106139 }, + { url = "https://files.pythonhosted.org/packages/86/cf/641787a0c64019e76eb8bea925930005960323f1a5539361c209613f4747/qdldl-0.1.7.post5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7e196871dafe4febb86c2886713c8a2226d19455226e56e3b9480aa78eb59b5e", size = 103421 }, + { url = "https://files.pythonhosted.org/packages/be/87/91d2f0debdd515b653c701c023b939325c51157d74154336b8495f156659/qdldl-0.1.7.post5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ba5ff31a66d1f92b41d0b97d27288d28a8c849dd6db2221a579b1a5a5a6df0f", size = 1179946 }, + { url = "https://files.pythonhosted.org/packages/b8/7e/5fe5a081bd229a2b703a4b93e5ecaf44f51902e9b6a645c8ce4ea325ec0d/qdldl-0.1.7.post5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c34872867c2bcac60279034594eac8dee042b9dedd4c45948e55884b8c5c9cd0", size = 1193311 }, + { url = "https://files.pythonhosted.org/packages/53/dc/d6b760217f0fa7007e45c03dc0193c828ee5010f037acb58b79cd0010fbc/qdldl-0.1.7.post5-cp311-cp311-win_amd64.whl", hash = "sha256:b1280e886f734e3d0d67f643e3d76c55d2e23d0e7b06d89b987681dc165892c5", size = 90488 }, + { url = "https://files.pythonhosted.org/packages/14/c1/eba61a848f9dfa0b54e954aa71f18eb35576f8842ef31dc76a3569a50526/qdldl-0.1.7.post5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d67a95d0ba73147a05cf98dc9284103f64150c9e2c214cd35ee0258f06922c5e", size = 106277 }, + { url = "https://files.pythonhosted.org/packages/02/2e/5daa29b8ecf25277c36a220ef3b509d2ec4079ab81ff3adc544bc12cd675/qdldl-0.1.7.post5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2e23d684427ce49f5d657e353322363555d1a31605fe72cbe4b965a4e260742c", size = 103179 }, + { url = "https://files.pythonhosted.org/packages/c6/74/5818f5027a0c252d1e8a2eba996359155d1518db90ce545f1becf0dd4a10/qdldl-0.1.7.post5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c4953d4fe61951fb515a6439009248b5a7b73627d74ee929d02b19bea41b19d", size = 1182542 }, + { url = "https://files.pythonhosted.org/packages/ae/55/90ad03c32e673a9b33cfa7cb43f55d1ab0509b60396afbb1031fa1516fd9/qdldl-0.1.7.post5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:520dbe4006a333c773ff474d2dc1e0af928c0dc7d9ca36db5637ba738ee608ba", size = 1201734 }, + { url = "https://files.pythonhosted.org/packages/c1/82/730d0d2c6093c4dc574947eea94e0cddeea836f43823a80fc8b064a82ddf/qdldl-0.1.7.post5-cp312-cp312-win_amd64.whl", hash = "sha256:13dfc0b225a5c180512488fa51f1771e8fa3c06d7fce9fd3c1d018bc03ba0eec", size = 90706 }, + { url = "https://files.pythonhosted.org/packages/d1/33/5c4348f6e2e4868ec843c05ec9679ec59b37cd9a251ee7ee9baf00b2f8b4/qdldl-0.1.7.post5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7600985d2321cb15f71f8bb3a92ef2a85284b4fd740d8bbd4960b8c2f7ee6d33", size = 106318 }, + { url = "https://files.pythonhosted.org/packages/c6/1a/aaa6835253c94a53816b4bea7c6051fd406c492bed7024fc683d0da1b939/qdldl-0.1.7.post5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:314153f574641c846a85ff9b4a5c0e0d23e32d0de11d8381866bb27577088bef", size = 103216 }, + { url = "https://files.pythonhosted.org/packages/9f/0e/fd317e269ba7c3ed11e0e999ebfe2f3611de9bdd1fe7052ea32f0a3fd22e/qdldl-0.1.7.post5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42b1ee3840d7d8ef4f1e3ffce0620116a71abd72c52ba46e0c194d4b294a0ad2", size = 1182628 }, + { url = "https://files.pythonhosted.org/packages/41/ad/6cd39b1c3ac5c0201eaf4be257303d1c6f7193f0d0f5c54ab151ba8faad9/qdldl-0.1.7.post5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cba6df1eedbaea844485e1c7a6ae9013bbdc86f07c4ebb13c89249b003de4ef4", size = 1201935 }, + { url = "https://files.pythonhosted.org/packages/08/f7/abac03a09f6848cee6d5dd7a7a8bd1dfed68766ee77f9cbf3e9de596ad68/qdldl-0.1.7.post5-cp313-cp313-win_amd64.whl", hash = "sha256:cc9be378e7bec67d4c62b7fa27cafb4f77d3e5e059d753c3dce0a5ae1ef5fea0", size = 90735 }, +] + +[[package]] +name = "regex" +version = "2024.11.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8e/5f/bd69653fbfb76cf8604468d3b4ec4c403197144c7bfe0e6a5fc9e02a07cb/regex-2024.11.6.tar.gz", hash = "sha256:7ab159b063c52a0333c884e4679f8d7a85112ee3078fe3d9004b2dd875585519", size = 399494 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/58/58/7e4d9493a66c88a7da6d205768119f51af0f684fe7be7bac8328e217a52c/regex-2024.11.6-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5478c6962ad548b54a591778e93cd7c456a7a29f8eca9c49e4f9a806dcc5d638", size = 482669 }, + { url = "https://files.pythonhosted.org/packages/34/4c/8f8e631fcdc2ff978609eaeef1d6994bf2f028b59d9ac67640ed051f1218/regex-2024.11.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2c89a8cc122b25ce6945f0423dc1352cb9593c68abd19223eebbd4e56612c5b7", size = 287684 }, + { url = "https://files.pythonhosted.org/packages/c5/1b/f0e4d13e6adf866ce9b069e191f303a30ab1277e037037a365c3aad5cc9c/regex-2024.11.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:94d87b689cdd831934fa3ce16cc15cd65748e6d689f5d2b8f4f4df2065c9fa20", size = 284589 }, + { url = "https://files.pythonhosted.org/packages/25/4d/ab21047f446693887f25510887e6820b93f791992994f6498b0318904d4a/regex-2024.11.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1062b39a0a2b75a9c694f7a08e7183a80c63c0d62b301418ffd9c35f55aaa114", size = 792121 }, + { url = "https://files.pythonhosted.org/packages/45/ee/c867e15cd894985cb32b731d89576c41a4642a57850c162490ea34b78c3b/regex-2024.11.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:167ed4852351d8a750da48712c3930b031f6efdaa0f22fa1933716bfcd6bf4a3", size = 831275 }, + { url = "https://files.pythonhosted.org/packages/b3/12/b0f480726cf1c60f6536fa5e1c95275a77624f3ac8fdccf79e6727499e28/regex-2024.11.6-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d548dafee61f06ebdb584080621f3e0c23fff312f0de1afc776e2a2ba99a74f", size = 818257 }, + { url = "https://files.pythonhosted.org/packages/bf/ce/0d0e61429f603bac433910d99ef1a02ce45a8967ffbe3cbee48599e62d88/regex-2024.11.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2a19f302cd1ce5dd01a9099aaa19cae6173306d1302a43b627f62e21cf18ac0", size = 792727 }, + { url = "https://files.pythonhosted.org/packages/e4/c1/243c83c53d4a419c1556f43777ccb552bccdf79d08fda3980e4e77dd9137/regex-2024.11.6-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bec9931dfb61ddd8ef2ebc05646293812cb6b16b60cf7c9511a832b6f1854b55", size = 780667 }, + { url = "https://files.pythonhosted.org/packages/c5/f4/75eb0dd4ce4b37f04928987f1d22547ddaf6c4bae697623c1b05da67a8aa/regex-2024.11.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9714398225f299aa85267fd222f7142fcb5c769e73d7733344efc46f2ef5cf89", size = 776963 }, + { url = "https://files.pythonhosted.org/packages/16/5d/95c568574e630e141a69ff8a254c2f188b4398e813c40d49228c9bbd9875/regex-2024.11.6-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:202eb32e89f60fc147a41e55cb086db2a3f8cb82f9a9a88440dcfc5d37faae8d", size = 784700 }, + { url = "https://files.pythonhosted.org/packages/8e/b5/f8495c7917f15cc6fee1e7f395e324ec3e00ab3c665a7dc9d27562fd5290/regex-2024.11.6-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:4181b814e56078e9b00427ca358ec44333765f5ca1b45597ec7446d3a1ef6e34", size = 848592 }, + { url = "https://files.pythonhosted.org/packages/1c/80/6dd7118e8cb212c3c60b191b932dc57db93fb2e36fb9e0e92f72a5909af9/regex-2024.11.6-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:068376da5a7e4da51968ce4c122a7cd31afaaec4fccc7856c92f63876e57b51d", size = 852929 }, + { url = "https://files.pythonhosted.org/packages/11/9b/5a05d2040297d2d254baf95eeeb6df83554e5e1df03bc1a6687fc4ba1f66/regex-2024.11.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ac10f2c4184420d881a3475fb2c6f4d95d53a8d50209a2500723d831036f7c45", size = 781213 }, + { url = "https://files.pythonhosted.org/packages/26/b7/b14e2440156ab39e0177506c08c18accaf2b8932e39fb092074de733d868/regex-2024.11.6-cp311-cp311-win32.whl", hash = "sha256:c36f9b6f5f8649bb251a5f3f66564438977b7ef8386a52460ae77e6070d309d9", size = 261734 }, + { url = "https://files.pythonhosted.org/packages/80/32/763a6cc01d21fb3819227a1cc3f60fd251c13c37c27a73b8ff4315433a8e/regex-2024.11.6-cp311-cp311-win_amd64.whl", hash = "sha256:02e28184be537f0e75c1f9b2f8847dc51e08e6e171c6bde130b2687e0c33cf60", size = 274052 }, + { url = "https://files.pythonhosted.org/packages/ba/30/9a87ce8336b172cc232a0db89a3af97929d06c11ceaa19d97d84fa90a8f8/regex-2024.11.6-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:52fb28f528778f184f870b7cf8f225f5eef0a8f6e3778529bdd40c7b3920796a", size = 483781 }, + { url = "https://files.pythonhosted.org/packages/01/e8/00008ad4ff4be8b1844786ba6636035f7ef926db5686e4c0f98093612add/regex-2024.11.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fdd6028445d2460f33136c55eeb1f601ab06d74cb3347132e1c24250187500d9", size = 288455 }, + { url = "https://files.pythonhosted.org/packages/60/85/cebcc0aff603ea0a201667b203f13ba75d9fc8668fab917ac5b2de3967bc/regex-2024.11.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:805e6b60c54bf766b251e94526ebad60b7de0c70f70a4e6210ee2891acb70bf2", size = 284759 }, + { url = "https://files.pythonhosted.org/packages/94/2b/701a4b0585cb05472a4da28ee28fdfe155f3638f5e1ec92306d924e5faf0/regex-2024.11.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b85c2530be953a890eaffde05485238f07029600e8f098cdf1848d414a8b45e4", size = 794976 }, + { url = "https://files.pythonhosted.org/packages/4b/bf/fa87e563bf5fee75db8915f7352e1887b1249126a1be4813837f5dbec965/regex-2024.11.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bb26437975da7dc36b7efad18aa9dd4ea569d2357ae6b783bf1118dabd9ea577", size = 833077 }, + { url = "https://files.pythonhosted.org/packages/a1/56/7295e6bad94b047f4d0834e4779491b81216583c00c288252ef625c01d23/regex-2024.11.6-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:abfa5080c374a76a251ba60683242bc17eeb2c9818d0d30117b4486be10c59d3", size = 823160 }, + { url = "https://files.pythonhosted.org/packages/fb/13/e3b075031a738c9598c51cfbc4c7879e26729c53aa9cca59211c44235314/regex-2024.11.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b7fa6606c2881c1db9479b0eaa11ed5dfa11c8d60a474ff0e095099f39d98e", size = 796896 }, + { url = "https://files.pythonhosted.org/packages/24/56/0b3f1b66d592be6efec23a795b37732682520b47c53da5a32c33ed7d84e3/regex-2024.11.6-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0c32f75920cf99fe6b6c539c399a4a128452eaf1af27f39bce8909c9a3fd8cbe", size = 783997 }, + { url = "https://files.pythonhosted.org/packages/f9/a1/eb378dada8b91c0e4c5f08ffb56f25fcae47bf52ad18f9b2f33b83e6d498/regex-2024.11.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:982e6d21414e78e1f51cf595d7f321dcd14de1f2881c5dc6a6e23bbbbd68435e", size = 781725 }, + { url = "https://files.pythonhosted.org/packages/83/f2/033e7dec0cfd6dda93390089864732a3409246ffe8b042e9554afa9bff4e/regex-2024.11.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a7c2155f790e2fb448faed6dd241386719802296ec588a8b9051c1f5c481bc29", size = 789481 }, + { url = "https://files.pythonhosted.org/packages/83/23/15d4552ea28990a74e7696780c438aadd73a20318c47e527b47a4a5a596d/regex-2024.11.6-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:149f5008d286636e48cd0b1dd65018548944e495b0265b45e1bffecce1ef7f39", size = 852896 }, + { url = "https://files.pythonhosted.org/packages/e3/39/ed4416bc90deedbfdada2568b2cb0bc1fdb98efe11f5378d9892b2a88f8f/regex-2024.11.6-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:e5364a4502efca094731680e80009632ad6624084aff9a23ce8c8c6820de3e51", size = 860138 }, + { url = "https://files.pythonhosted.org/packages/93/2d/dd56bb76bd8e95bbce684326302f287455b56242a4f9c61f1bc76e28360e/regex-2024.11.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0a86e7eeca091c09e021db8eb72d54751e527fa47b8d5787caf96d9831bd02ad", size = 787692 }, + { url = "https://files.pythonhosted.org/packages/0b/55/31877a249ab7a5156758246b9c59539abbeba22461b7d8adc9e8475ff73e/regex-2024.11.6-cp312-cp312-win32.whl", hash = "sha256:32f9a4c643baad4efa81d549c2aadefaeba12249b2adc5af541759237eee1c54", size = 262135 }, + { url = "https://files.pythonhosted.org/packages/38/ec/ad2d7de49a600cdb8dd78434a1aeffe28b9d6fc42eb36afab4a27ad23384/regex-2024.11.6-cp312-cp312-win_amd64.whl", hash = "sha256:a93c194e2df18f7d264092dc8539b8ffb86b45b899ab976aa15d48214138e81b", size = 273567 }, + { url = "https://files.pythonhosted.org/packages/90/73/bcb0e36614601016552fa9344544a3a2ae1809dc1401b100eab02e772e1f/regex-2024.11.6-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a6ba92c0bcdf96cbf43a12c717eae4bc98325ca3730f6b130ffa2e3c3c723d84", size = 483525 }, + { url = "https://files.pythonhosted.org/packages/0f/3f/f1a082a46b31e25291d830b369b6b0c5576a6f7fb89d3053a354c24b8a83/regex-2024.11.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:525eab0b789891ac3be914d36893bdf972d483fe66551f79d3e27146191a37d4", size = 288324 }, + { url = "https://files.pythonhosted.org/packages/09/c9/4e68181a4a652fb3ef5099e077faf4fd2a694ea6e0f806a7737aff9e758a/regex-2024.11.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:086a27a0b4ca227941700e0b31425e7a28ef1ae8e5e05a33826e17e47fbfdba0", size = 284617 }, + { url = "https://files.pythonhosted.org/packages/fc/fd/37868b75eaf63843165f1d2122ca6cb94bfc0271e4428cf58c0616786dce/regex-2024.11.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bde01f35767c4a7899b7eb6e823b125a64de314a8ee9791367c9a34d56af18d0", size = 795023 }, + { url = "https://files.pythonhosted.org/packages/c4/7c/d4cd9c528502a3dedb5c13c146e7a7a539a3853dc20209c8e75d9ba9d1b2/regex-2024.11.6-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b583904576650166b3d920d2bcce13971f6f9e9a396c673187f49811b2769dc7", size = 833072 }, + { url = "https://files.pythonhosted.org/packages/4f/db/46f563a08f969159c5a0f0e722260568425363bea43bb7ae370becb66a67/regex-2024.11.6-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c4de13f06a0d54fa0d5ab1b7138bfa0d883220965a29616e3ea61b35d5f5fc7", size = 823130 }, + { url = "https://files.pythonhosted.org/packages/db/60/1eeca2074f5b87df394fccaa432ae3fc06c9c9bfa97c5051aed70e6e00c2/regex-2024.11.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3cde6e9f2580eb1665965ce9bf17ff4952f34f5b126beb509fee8f4e994f143c", size = 796857 }, + { url = "https://files.pythonhosted.org/packages/10/db/ac718a08fcee981554d2f7bb8402f1faa7e868c1345c16ab1ebec54b0d7b/regex-2024.11.6-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0d7f453dca13f40a02b79636a339c5b62b670141e63efd511d3f8f73fba162b3", size = 784006 }, + { url = "https://files.pythonhosted.org/packages/c2/41/7da3fe70216cea93144bf12da2b87367590bcf07db97604edeea55dac9ad/regex-2024.11.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:59dfe1ed21aea057a65c6b586afd2a945de04fc7db3de0a6e3ed5397ad491b07", size = 781650 }, + { url = "https://files.pythonhosted.org/packages/a7/d5/880921ee4eec393a4752e6ab9f0fe28009435417c3102fc413f3fe81c4e5/regex-2024.11.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b97c1e0bd37c5cd7902e65f410779d39eeda155800b65fc4d04cc432efa9bc6e", size = 789545 }, + { url = "https://files.pythonhosted.org/packages/dc/96/53770115e507081122beca8899ab7f5ae28ae790bfcc82b5e38976df6a77/regex-2024.11.6-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f9d1e379028e0fc2ae3654bac3cbbef81bf3fd571272a42d56c24007979bafb6", size = 853045 }, + { url = "https://files.pythonhosted.org/packages/31/d3/1372add5251cc2d44b451bd94f43b2ec78e15a6e82bff6a290ef9fd8f00a/regex-2024.11.6-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:13291b39131e2d002a7940fb176e120bec5145f3aeb7621be6534e46251912c4", size = 860182 }, + { url = "https://files.pythonhosted.org/packages/ed/e3/c446a64984ea9f69982ba1a69d4658d5014bc7a0ea468a07e1a1265db6e2/regex-2024.11.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4f51f88c126370dcec4908576c5a627220da6c09d0bff31cfa89f2523843316d", size = 787733 }, + { url = "https://files.pythonhosted.org/packages/2b/f1/e40c8373e3480e4f29f2692bd21b3e05f296d3afebc7e5dcf21b9756ca1c/regex-2024.11.6-cp313-cp313-win32.whl", hash = "sha256:63b13cfd72e9601125027202cad74995ab26921d8cd935c25f09c630436348ff", size = 262122 }, + { url = "https://files.pythonhosted.org/packages/45/94/bc295babb3062a731f52621cdc992d123111282e291abaf23faa413443ea/regex-2024.11.6-cp313-cp313-win_amd64.whl", hash = "sha256:2b3361af3198667e99927da8b84c1b010752fa4b1115ee30beaa332cabc3ef1a", size = 273545 }, +] + +[[package]] +name = "requests" +version = "2.32.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928 }, +] + +[package.optional-dependencies] +socks = [ + { name = "pysocks" }, +] + +[[package]] +name = "ruff" +version = "0.11.12" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/15/0a/92416b159ec00cdf11e5882a9d80d29bf84bba3dbebc51c4898bfbca1da6/ruff-0.11.12.tar.gz", hash = "sha256:43cf7f69c7d7c7d7513b9d59c5d8cafd704e05944f978614aa9faff6ac202603", size = 4202289 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/60/cc/53eb79f012d15e136d40a8e8fc519ba8f55a057f60b29c2df34efd47c6e3/ruff-0.11.12-py3-none-linux_armv6l.whl", hash = "sha256:c7680aa2f0d4c4f43353d1e72123955c7a2159b8646cd43402de6d4a3a25d7cc", size = 10285597 }, + { url = "https://files.pythonhosted.org/packages/e7/d7/73386e9fb0232b015a23f62fea7503f96e29c29e6c45461d4a73bac74df9/ruff-0.11.12-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:2cad64843da9f134565c20bcc430642de897b8ea02e2e79e6e02a76b8dcad7c3", size = 11053154 }, + { url = "https://files.pythonhosted.org/packages/4e/eb/3eae144c5114e92deb65a0cb2c72326c8469e14991e9bc3ec0349da1331c/ruff-0.11.12-py3-none-macosx_11_0_arm64.whl", hash = "sha256:9b6886b524a1c659cee1758140138455d3c029783d1b9e643f3624a5ee0cb0aa", size = 10403048 }, + { url = "https://files.pythonhosted.org/packages/29/64/20c54b20e58b1058db6689e94731f2a22e9f7abab74e1a758dfba058b6ca/ruff-0.11.12-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cc3a3690aad6e86c1958d3ec3c38c4594b6ecec75c1f531e84160bd827b2012", size = 10597062 }, + { url = "https://files.pythonhosted.org/packages/29/3a/79fa6a9a39422a400564ca7233a689a151f1039110f0bbbabcb38106883a/ruff-0.11.12-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f97fdbc2549f456c65b3b0048560d44ddd540db1f27c778a938371424b49fe4a", size = 10155152 }, + { url = "https://files.pythonhosted.org/packages/e5/a4/22c2c97b2340aa968af3a39bc38045e78d36abd4ed3fa2bde91c31e712e3/ruff-0.11.12-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:74adf84960236961090e2d1348c1a67d940fd12e811a33fb3d107df61eef8fc7", size = 11723067 }, + { url = "https://files.pythonhosted.org/packages/bc/cf/3e452fbd9597bcd8058856ecd42b22751749d07935793a1856d988154151/ruff-0.11.12-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:b56697e5b8bcf1d61293ccfe63873aba08fdbcbbba839fc046ec5926bdb25a3a", size = 12460807 }, + { url = "https://files.pythonhosted.org/packages/2f/ec/8f170381a15e1eb7d93cb4feef8d17334d5a1eb33fee273aee5d1f8241a3/ruff-0.11.12-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4d47afa45e7b0eaf5e5969c6b39cbd108be83910b5c74626247e366fd7a36a13", size = 12063261 }, + { url = "https://files.pythonhosted.org/packages/0d/bf/57208f8c0a8153a14652a85f4116c0002148e83770d7a41f2e90b52d2b4e/ruff-0.11.12-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:692bf9603fe1bf949de8b09a2da896f05c01ed7a187f4a386cdba6760e7f61be", size = 11329601 }, + { url = "https://files.pythonhosted.org/packages/c3/56/edf942f7fdac5888094d9ffa303f12096f1a93eb46570bcf5f14c0c70880/ruff-0.11.12-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:08033320e979df3b20dba567c62f69c45e01df708b0f9c83912d7abd3e0801cd", size = 11522186 }, + { url = "https://files.pythonhosted.org/packages/ed/63/79ffef65246911ed7e2290aeece48739d9603b3a35f9529fec0fc6c26400/ruff-0.11.12-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:929b7706584f5bfd61d67d5070f399057d07c70585fa8c4491d78ada452d3bef", size = 10449032 }, + { url = "https://files.pythonhosted.org/packages/88/19/8c9d4d8a1c2a3f5a1ea45a64b42593d50e28b8e038f1aafd65d6b43647f3/ruff-0.11.12-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:7de4a73205dc5756b8e09ee3ed67c38312dce1aa28972b93150f5751199981b5", size = 10129370 }, + { url = "https://files.pythonhosted.org/packages/bc/0f/2d15533eaa18f460530a857e1778900cd867ded67f16c85723569d54e410/ruff-0.11.12-py3-none-musllinux_1_2_i686.whl", hash = "sha256:2635c2a90ac1b8ca9e93b70af59dfd1dd2026a40e2d6eebaa3efb0465dd9cf02", size = 11123529 }, + { url = "https://files.pythonhosted.org/packages/4f/e2/4c2ac669534bdded835356813f48ea33cfb3a947dc47f270038364587088/ruff-0.11.12-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:d05d6a78a89166f03f03a198ecc9d18779076ad0eec476819467acb401028c0c", size = 11577642 }, + { url = "https://files.pythonhosted.org/packages/a7/9b/c9ddf7f924d5617a1c94a93ba595f4b24cb5bc50e98b94433ab3f7ad27e5/ruff-0.11.12-py3-none-win32.whl", hash = "sha256:f5a07f49767c4be4772d161bfc049c1f242db0cfe1bd976e0f0886732a4765d6", size = 10475511 }, + { url = "https://files.pythonhosted.org/packages/fd/d6/74fb6d3470c1aada019ffff33c0f9210af746cca0a4de19a1f10ce54968a/ruff-0.11.12-py3-none-win_amd64.whl", hash = "sha256:5a4d9f8030d8c3a45df201d7fb3ed38d0219bccd7955268e863ee4a115fa0832", size = 11523573 }, + { url = "https://files.pythonhosted.org/packages/44/42/d58086ec20f52d2b0140752ae54b355ea2be2ed46f914231136dd1effcc7/ruff-0.11.12-py3-none-win_arm64.whl", hash = "sha256:65194e37853158d368e333ba282217941029a28ea90913c67e558c611d04daa5", size = 10697770 }, +] + +[[package]] +name = "sacremoses" +version = "0.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "joblib" }, + { name = "regex" }, + { name = "tqdm" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1d/51/fbdc4af4f6e85d26169e28be3763fe50ddfd0d4bf8b871422b0788dcc4d2/sacremoses-0.1.1.tar.gz", hash = "sha256:b6fd5d3a766b02154ed80b962ddca91e1fd25629c0978c7efba21ebccf663934", size = 883188 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/f0/89ee2bc9da434bd78464f288fdb346bc2932f2ee80a90b2a4bbbac262c74/sacremoses-0.1.1-py3-none-any.whl", hash = "sha256:31e04c98b169bfd902144824d191825cd69220cdb4ae4bcf1ec58a7db5587b1a", size = 897476 }, +] + +[[package]] +name = "safetensors" +version = "0.5.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/71/7e/2d5d6ee7b40c0682315367ec7475693d110f512922d582fef1bd4a63adc3/safetensors-0.5.3.tar.gz", hash = "sha256:b6b0d6ecacec39a4fdd99cc19f4576f5219ce858e6fd8dbe7609df0b8dc56965", size = 67210 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/ae/88f6c49dbd0cc4da0e08610019a3c78a7d390879a919411a410a1876d03a/safetensors-0.5.3-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:bd20eb133db8ed15b40110b7c00c6df51655a2998132193de2f75f72d99c7073", size = 436917 }, + { url = "https://files.pythonhosted.org/packages/b8/3b/11f1b4a2f5d2ab7da34ecc062b0bc301f2be024d110a6466726bec8c055c/safetensors-0.5.3-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:21d01c14ff6c415c485616b8b0bf961c46b3b343ca59110d38d744e577f9cce7", size = 418419 }, + { url = "https://files.pythonhosted.org/packages/5d/9a/add3e6fef267658075c5a41573c26d42d80c935cdc992384dfae435feaef/safetensors-0.5.3-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:11bce6164887cd491ca75c2326a113ba934be596e22b28b1742ce27b1d076467", size = 459493 }, + { url = "https://files.pythonhosted.org/packages/df/5c/bf2cae92222513cc23b3ff85c4a1bb2811a2c3583ac0f8e8d502751de934/safetensors-0.5.3-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4a243be3590bc3301c821da7a18d87224ef35cbd3e5f5727e4e0728b8172411e", size = 472400 }, + { url = "https://files.pythonhosted.org/packages/58/11/7456afb740bd45782d0f4c8e8e1bb9e572f1bf82899fb6ace58af47b4282/safetensors-0.5.3-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8bd84b12b1670a6f8e50f01e28156422a2bc07fb16fc4e98bded13039d688a0d", size = 522891 }, + { url = "https://files.pythonhosted.org/packages/57/3d/fe73a9d2ace487e7285f6e157afee2383bd1ddb911b7cb44a55cf812eae3/safetensors-0.5.3-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:391ac8cab7c829452175f871fcaf414aa1e292b5448bd02620f675a7f3e7abb9", size = 537694 }, + { url = "https://files.pythonhosted.org/packages/a6/f8/dae3421624fcc87a89d42e1898a798bc7ff72c61f38973a65d60df8f124c/safetensors-0.5.3-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cead1fa41fc54b1e61089fa57452e8834f798cb1dc7a09ba3524f1eb08e0317a", size = 471642 }, + { url = "https://files.pythonhosted.org/packages/ce/20/1fbe16f9b815f6c5a672f5b760951e20e17e43f67f231428f871909a37f6/safetensors-0.5.3-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1077f3e94182d72618357b04b5ced540ceb71c8a813d3319f1aba448e68a770d", size = 502241 }, + { url = "https://files.pythonhosted.org/packages/5f/18/8e108846b506487aa4629fe4116b27db65c3dde922de2c8e0cc1133f3f29/safetensors-0.5.3-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:799021e78287bac619c7b3f3606730a22da4cda27759ddf55d37c8db7511c74b", size = 638001 }, + { url = "https://files.pythonhosted.org/packages/82/5a/c116111d8291af6c8c8a8b40628fe833b9db97d8141c2a82359d14d9e078/safetensors-0.5.3-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:df26da01aaac504334644e1b7642fa000bfec820e7cef83aeac4e355e03195ff", size = 734013 }, + { url = "https://files.pythonhosted.org/packages/7d/ff/41fcc4d3b7de837963622e8610d998710705bbde9a8a17221d85e5d0baad/safetensors-0.5.3-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:32c3ef2d7af8b9f52ff685ed0bc43913cdcde135089ae322ee576de93eae5135", size = 670687 }, + { url = "https://files.pythonhosted.org/packages/40/ad/2b113098e69c985a3d8fbda4b902778eae4a35b7d5188859b4a63d30c161/safetensors-0.5.3-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:37f1521be045e56fc2b54c606d4455573e717b2d887c579ee1dbba5f868ece04", size = 643147 }, + { url = "https://files.pythonhosted.org/packages/0a/0c/95aeb51d4246bd9a3242d3d8349c1112b4ee7611a4b40f0c5c93b05f001d/safetensors-0.5.3-cp38-abi3-win32.whl", hash = "sha256:cfc0ec0846dcf6763b0ed3d1846ff36008c6e7290683b61616c4b040f6a54ace", size = 296677 }, + { url = "https://files.pythonhosted.org/packages/69/e2/b011c38e5394c4c18fb5500778a55ec43ad6106126e74723ffaee246f56e/safetensors-0.5.3-cp38-abi3-win_amd64.whl", hash = "sha256:836cbbc320b47e80acd40e44c8682db0e8ad7123209f69b093def21ec7cafd11", size = 308878 }, +] + +[[package]] +name = "scikit-image" +version = "0.25.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "imageio" }, + { name = "lazy-loader" }, + { name = "networkx" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "pillow" }, + { name = "scipy" }, + { name = "tifffile" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c7/a8/3c0f256012b93dd2cb6fda9245e9f4bff7dc0486880b248005f15ea2255e/scikit_image-0.25.2.tar.gz", hash = "sha256:e5a37e6cd4d0c018a7a55b9d601357e3382826d3888c10d0213fc63bff977dde", size = 22693594 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c4/97/3051c68b782ee3f1fb7f8f5bb7d535cf8cb92e8aae18fa9c1cdf7e15150d/scikit_image-0.25.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f4bac9196fb80d37567316581c6060763b0f4893d3aca34a9ede3825bc035b17", size = 14003057 }, + { url = "https://files.pythonhosted.org/packages/19/23/257fc696c562639826065514d551b7b9b969520bd902c3a8e2fcff5b9e17/scikit_image-0.25.2-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:d989d64ff92e0c6c0f2018c7495a5b20e2451839299a018e0e5108b2680f71e0", size = 13180335 }, + { url = "https://files.pythonhosted.org/packages/ef/14/0c4a02cb27ca8b1e836886b9ec7c9149de03053650e9e2ed0625f248dd92/scikit_image-0.25.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2cfc96b27afe9a05bc92f8c6235321d3a66499995675b27415e0d0c76625173", size = 14144783 }, + { url = "https://files.pythonhosted.org/packages/dd/9b/9fb556463a34d9842491d72a421942c8baff4281025859c84fcdb5e7e602/scikit_image-0.25.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24cc986e1f4187a12aa319f777b36008764e856e5013666a4a83f8df083c2641", size = 14785376 }, + { url = "https://files.pythonhosted.org/packages/de/ec/b57c500ee85885df5f2188f8bb70398481393a69de44a00d6f1d055f103c/scikit_image-0.25.2-cp311-cp311-win_amd64.whl", hash = "sha256:b4f6b61fc2db6340696afe3db6b26e0356911529f5f6aee8c322aa5157490c9b", size = 12791698 }, + { url = "https://files.pythonhosted.org/packages/35/8c/5df82881284459f6eec796a5ac2a0a304bb3384eec2e73f35cfdfcfbf20c/scikit_image-0.25.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:8db8dd03663112783221bf01ccfc9512d1cc50ac9b5b0fe8f4023967564719fb", size = 13986000 }, + { url = "https://files.pythonhosted.org/packages/ce/e6/93bebe1abcdce9513ffec01d8af02528b4c41fb3c1e46336d70b9ed4ef0d/scikit_image-0.25.2-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:483bd8cc10c3d8a7a37fae36dfa5b21e239bd4ee121d91cad1f81bba10cfb0ed", size = 13235893 }, + { url = "https://files.pythonhosted.org/packages/53/4b/eda616e33f67129e5979a9eb33c710013caa3aa8a921991e6cc0b22cea33/scikit_image-0.25.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d1e80107bcf2bf1291acfc0bf0425dceb8890abe9f38d8e94e23497cbf7ee0d", size = 14178389 }, + { url = "https://files.pythonhosted.org/packages/6b/b5/b75527c0f9532dd8a93e8e7cd8e62e547b9f207d4c11e24f0006e8646b36/scikit_image-0.25.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a17e17eb8562660cc0d31bb55643a4da996a81944b82c54805c91b3fe66f4824", size = 15003435 }, + { url = "https://files.pythonhosted.org/packages/34/e3/49beb08ebccda3c21e871b607c1cb2f258c3fa0d2f609fed0a5ba741b92d/scikit_image-0.25.2-cp312-cp312-win_amd64.whl", hash = "sha256:bdd2b8c1de0849964dbc54037f36b4e9420157e67e45a8709a80d727f52c7da2", size = 12899474 }, + { url = "https://files.pythonhosted.org/packages/e6/7c/9814dd1c637f7a0e44342985a76f95a55dd04be60154247679fd96c7169f/scikit_image-0.25.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7efa888130f6c548ec0439b1a7ed7295bc10105458a421e9bf739b457730b6da", size = 13921841 }, + { url = "https://files.pythonhosted.org/packages/84/06/66a2e7661d6f526740c309e9717d3bd07b473661d5cdddef4dd978edab25/scikit_image-0.25.2-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:dd8011efe69c3641920614d550f5505f83658fe33581e49bed86feab43a180fc", size = 13196862 }, + { url = "https://files.pythonhosted.org/packages/4e/63/3368902ed79305f74c2ca8c297dfeb4307269cbe6402412668e322837143/scikit_image-0.25.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28182a9d3e2ce3c2e251383bdda68f8d88d9fff1a3ebe1eb61206595c9773341", size = 14117785 }, + { url = "https://files.pythonhosted.org/packages/cd/9b/c3da56a145f52cd61a68b8465d6a29d9503bc45bc993bb45e84371c97d94/scikit_image-0.25.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8abd3c805ce6944b941cfed0406d88faeb19bab3ed3d4b50187af55cf24d147", size = 14977119 }, + { url = "https://files.pythonhosted.org/packages/8a/97/5fcf332e1753831abb99a2525180d3fb0d70918d461ebda9873f66dcc12f/scikit_image-0.25.2-cp313-cp313-win_amd64.whl", hash = "sha256:64785a8acefee460ec49a354706db0b09d1f325674107d7fa3eadb663fb56d6f", size = 12885116 }, + { url = "https://files.pythonhosted.org/packages/10/cc/75e9f17e3670b5ed93c32456fda823333c6279b144cd93e2c03aa06aa472/scikit_image-0.25.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:330d061bd107d12f8d68f1d611ae27b3b813b8cdb0300a71d07b1379178dd4cd", size = 13862801 }, +] + +[[package]] +name = "scikit-learn" +version = "1.6.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "joblib" }, + { name = "numpy" }, + { name = "scipy" }, + { name = "threadpoolctl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9e/a5/4ae3b3a0755f7b35a280ac90b28817d1f380318973cff14075ab41ef50d9/scikit_learn-1.6.1.tar.gz", hash = "sha256:b4fc2525eca2c69a59260f583c56a7557c6ccdf8deafdba6e060f94c1c59738e", size = 7068312 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6c/2a/e291c29670795406a824567d1dfc91db7b699799a002fdaa452bceea8f6e/scikit_learn-1.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:72abc587c75234935e97d09aa4913a82f7b03ee0b74111dcc2881cba3c5a7b33", size = 12102620 }, + { url = "https://files.pythonhosted.org/packages/25/92/ee1d7a00bb6b8c55755d4984fd82608603a3cc59959245068ce32e7fb808/scikit_learn-1.6.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:b3b00cdc8f1317b5f33191df1386c0befd16625f49d979fe77a8d44cae82410d", size = 11116234 }, + { url = "https://files.pythonhosted.org/packages/30/cd/ed4399485ef364bb25f388ab438e3724e60dc218c547a407b6e90ccccaef/scikit_learn-1.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc4765af3386811c3ca21638f63b9cf5ecf66261cc4815c1db3f1e7dc7b79db2", size = 12592155 }, + { url = "https://files.pythonhosted.org/packages/a8/f3/62fc9a5a659bb58a03cdd7e258956a5824bdc9b4bb3c5d932f55880be569/scikit_learn-1.6.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:25fc636bdaf1cc2f4a124a116312d837148b5e10872147bdaf4887926b8c03d8", size = 13497069 }, + { url = "https://files.pythonhosted.org/packages/a1/a6/c5b78606743a1f28eae8f11973de6613a5ee87366796583fb74c67d54939/scikit_learn-1.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:fa909b1a36e000a03c382aade0bd2063fd5680ff8b8e501660c0f59f021a6415", size = 11139809 }, + { url = "https://files.pythonhosted.org/packages/0a/18/c797c9b8c10380d05616db3bfb48e2a3358c767affd0857d56c2eb501caa/scikit_learn-1.6.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:926f207c804104677af4857b2c609940b743d04c4c35ce0ddc8ff4f053cddc1b", size = 12104516 }, + { url = "https://files.pythonhosted.org/packages/c4/b7/2e35f8e289ab70108f8cbb2e7a2208f0575dc704749721286519dcf35f6f/scikit_learn-1.6.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:2c2cae262064e6a9b77eee1c8e768fc46aa0b8338c6a8297b9b6759720ec0ff2", size = 11167837 }, + { url = "https://files.pythonhosted.org/packages/a4/f6/ff7beaeb644bcad72bcfd5a03ff36d32ee4e53a8b29a639f11bcb65d06cd/scikit_learn-1.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1061b7c028a8663fb9a1a1baf9317b64a257fcb036dae5c8752b2abef31d136f", size = 12253728 }, + { url = "https://files.pythonhosted.org/packages/29/7a/8bce8968883e9465de20be15542f4c7e221952441727c4dad24d534c6d99/scikit_learn-1.6.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e69fab4ebfc9c9b580a7a80111b43d214ab06250f8a7ef590a4edf72464dd86", size = 13147700 }, + { url = "https://files.pythonhosted.org/packages/62/27/585859e72e117fe861c2079bcba35591a84f801e21bc1ab85bce6ce60305/scikit_learn-1.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:70b1d7e85b1c96383f872a519b3375f92f14731e279a7b4c6cfd650cf5dffc52", size = 11110613 }, + { url = "https://files.pythonhosted.org/packages/2e/59/8eb1872ca87009bdcdb7f3cdc679ad557b992c12f4b61f9250659e592c63/scikit_learn-1.6.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2ffa1e9e25b3d93990e74a4be2c2fc61ee5af85811562f1288d5d055880c4322", size = 12010001 }, + { url = "https://files.pythonhosted.org/packages/9d/05/f2fc4effc5b32e525408524c982c468c29d22f828834f0625c5ef3d601be/scikit_learn-1.6.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:dc5cf3d68c5a20ad6d571584c0750ec641cc46aeef1c1507be51300e6003a7e1", size = 11096360 }, + { url = "https://files.pythonhosted.org/packages/c8/e4/4195d52cf4f113573fb8ebc44ed5a81bd511a92c0228889125fac2f4c3d1/scikit_learn-1.6.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c06beb2e839ecc641366000ca84f3cf6fa9faa1777e29cf0c04be6e4d096a348", size = 12209004 }, + { url = "https://files.pythonhosted.org/packages/94/be/47e16cdd1e7fcf97d95b3cb08bde1abb13e627861af427a3651fcb80b517/scikit_learn-1.6.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e8ca8cb270fee8f1f76fa9bfd5c3507d60c6438bbee5687f81042e2bb98e5a97", size = 13171776 }, + { url = "https://files.pythonhosted.org/packages/34/b0/ca92b90859070a1487827dbc672f998da95ce83edce1270fc23f96f1f61a/scikit_learn-1.6.1-cp313-cp313-win_amd64.whl", hash = "sha256:7a1c43c8ec9fde528d664d947dc4c0789be4077a3647f232869f41d9bf50e0fb", size = 11071865 }, + { url = "https://files.pythonhosted.org/packages/12/ae/993b0fb24a356e71e9a894e42b8a9eec528d4c70217353a1cd7a48bc25d4/scikit_learn-1.6.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a17c1dea1d56dcda2fac315712f3651a1fea86565b64b48fa1bc090249cbf236", size = 11955804 }, + { url = "https://files.pythonhosted.org/packages/d6/54/32fa2ee591af44507eac86406fa6bba968d1eb22831494470d0a2e4a1eb1/scikit_learn-1.6.1-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:6a7aa5f9908f0f28f4edaa6963c0a6183f1911e63a69aa03782f0d924c830a35", size = 11100530 }, + { url = "https://files.pythonhosted.org/packages/3f/58/55856da1adec655bdce77b502e94a267bf40a8c0b89f8622837f89503b5a/scikit_learn-1.6.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0650e730afb87402baa88afbf31c07b84c98272622aaba002559b614600ca691", size = 12433852 }, + { url = "https://files.pythonhosted.org/packages/ff/4f/c83853af13901a574f8f13b645467285a48940f185b690936bb700a50863/scikit_learn-1.6.1-cp313-cp313t-win_amd64.whl", hash = "sha256:3f59fe08dc03ea158605170eb52b22a105f238a5d512c4470ddeca71feae8e5f", size = 11337256 }, +] + +[[package]] +name = "scikit-survival" +version = "0.24.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "ecos" }, + { name = "joblib" }, + { name = "numexpr" }, + { name = "numpy" }, + { name = "osqp" }, + { name = "pandas" }, + { name = "scikit-learn" }, + { name = "scipy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c7/cb/0d3aa99c3997f22b0561900c06726c97a1f0799d17ed64fff98c7b1c2f58/scikit_survival-0.24.1.tar.gz", hash = "sha256:059ca5911f980e44f69951baf08efc8d7a7cf1adba7a5422580ef65330cfd88e", size = 2819926 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f7/42/19b84de0eb6af19cf6005d2a192d680eadc31aa15bbaf3435663fed6ebc3/scikit_survival-0.24.1-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:eb2902f6da4eb65751a0fe5efb539c0468e012e980c198f4bc956e7339c22996", size = 868890 }, + { url = "https://files.pythonhosted.org/packages/7e/18/4d29805f94e793799512f469a8e0cc97a3d4d73057c4052e62c0781d819a/scikit_survival-0.24.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:51a624760bf88df0a469d363737394c1d45aedd9eaaeb0fedb76d09af8fa8b90", size = 842118 }, + { url = "https://files.pythonhosted.org/packages/73/f4/b474631b68a5a7fe799cc63dc30d9db5432d16e24ac760e6560aeb84aff9/scikit_survival-0.24.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d686d0c94c9ad919c32bd02ccacd89d6d7a296b9deab1f9b797bb670350856e6", size = 3924338 }, + { url = "https://files.pythonhosted.org/packages/cf/e4/8fbba4d983c619aff7235618600e9c32395b007d29db14973066a7e952ff/scikit_survival-0.24.1-cp311-cp311-win_amd64.whl", hash = "sha256:bc77a649fe960f0f00aa09b6c13b125f5d893451775fe1cfd14f49697d7b885a", size = 826727 }, + { url = "https://files.pythonhosted.org/packages/3d/7e/a4f0f223246e52a91d2ca6f0a1283a4578160febbec2065ab128d4e3303d/scikit_survival-0.24.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4f44b6fd3147fa41771977317be990ff44c471513f0e42191db2b88248f7bd72", size = 876169 }, + { url = "https://files.pythonhosted.org/packages/c1/e4/5090bf732722b6190b79d8cf39a12bee40b1873a2d4f1d4427342ac0e377/scikit_survival-0.24.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9e0c690394ad58ff8d20f0525eb5eae93966ed9531f0956a105b66a7503e4ac4", size = 847521 }, + { url = "https://files.pythonhosted.org/packages/67/d2/8e3a115ff6adc5ace14b5cf32a2d8d77b32041ebeb2bad84fb82038dacbe/scikit_survival-0.24.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e06abac35705b471d950fa491bbb0f7473fe9005f753b2996819b231dc0380ec", size = 3885151 }, + { url = "https://files.pythonhosted.org/packages/01/34/6d6b2554c1d3c27371fa4ed8719f2d659802d11f333a26d0e7ee854e9197/scikit_survival-0.24.1-cp312-cp312-win_amd64.whl", hash = "sha256:62d8d37904d38a7140b8918557d55cc8d3bfe832fd39de63e723b0aad8b9bb21", size = 832557 }, + { url = "https://files.pythonhosted.org/packages/eb/03/a8fe9119ac7dbac03b7554ddeaf632680f7a7d005670a9f1f363ce847bf7/scikit_survival-0.24.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1a0126081f84b45804359fc0a68ad4c3a2dbee88656baa0b78fa304b52a03b23", size = 869938 }, + { url = "https://files.pythonhosted.org/packages/ab/d7/554c7fe38e52f1895f0bdfd2de96e4c17da55f23f7264cc1fab34c731a4d/scikit_survival-0.24.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1332d310d9bfed12932989c869e6e5757e4a20ab01a89f25b64eb83049d1098d", size = 841411 }, + { url = "https://files.pythonhosted.org/packages/30/3f/d6fa1a1f64cb5d09d33d476779809fed05d2971c5ffd07de4a471ad3f1a0/scikit_survival-0.24.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e978b79bc542e259cf75024b1b6381f2636122ebe440a64fd7aed7f8a9f1afc0", size = 3861630 }, + { url = "https://files.pythonhosted.org/packages/b9/76/ce24305c24ae89ceff0e8ed7305cf5542016cded3bbb57fc599a2d459028/scikit_survival-0.24.1-cp313-cp313-win_amd64.whl", hash = "sha256:4fa5fdb77c7902b90a353e04655245bd3b81dc980743b7f83ad68cb74ed63d83", size = 830658 }, +] + +[[package]] +name = "scipy" +version = "1.15.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0f/37/6964b830433e654ec7485e45a00fc9a27cf868d622838f6b6d9c5ec0d532/scipy-1.15.3.tar.gz", hash = "sha256:eae3cf522bc7df64b42cad3925c876e1b0b6c35c1337c93e12c0f366f55b0eaf", size = 59419214 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/96/ab/5cc9f80f28f6a7dff646c5756e559823614a42b1939d86dd0ed550470210/scipy-1.15.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:993439ce220d25e3696d1b23b233dd010169b62f6456488567e830654ee37a6b", size = 38714255 }, + { url = "https://files.pythonhosted.org/packages/4a/4a/66ba30abe5ad1a3ad15bfb0b59d22174012e8056ff448cb1644deccbfed2/scipy-1.15.3-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:34716e281f181a02341ddeaad584205bd2fd3c242063bd3423d61ac259ca7eba", size = 30111035 }, + { url = "https://files.pythonhosted.org/packages/4b/fa/a7e5b95afd80d24313307f03624acc65801846fa75599034f8ceb9e2cbf6/scipy-1.15.3-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3b0334816afb8b91dab859281b1b9786934392aa3d527cd847e41bb6f45bee65", size = 22384499 }, + { url = "https://files.pythonhosted.org/packages/17/99/f3aaddccf3588bb4aea70ba35328c204cadd89517a1612ecfda5b2dd9d7a/scipy-1.15.3-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:6db907c7368e3092e24919b5e31c76998b0ce1684d51a90943cb0ed1b4ffd6c1", size = 25152602 }, + { url = "https://files.pythonhosted.org/packages/56/c5/1032cdb565f146109212153339f9cb8b993701e9fe56b1c97699eee12586/scipy-1.15.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:721d6b4ef5dc82ca8968c25b111e307083d7ca9091bc38163fb89243e85e3889", size = 35503415 }, + { url = "https://files.pythonhosted.org/packages/bd/37/89f19c8c05505d0601ed5650156e50eb881ae3918786c8fd7262b4ee66d3/scipy-1.15.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39cb9c62e471b1bb3750066ecc3a3f3052b37751c7c3dfd0fd7e48900ed52982", size = 37652622 }, + { url = "https://files.pythonhosted.org/packages/7e/31/be59513aa9695519b18e1851bb9e487de66f2d31f835201f1b42f5d4d475/scipy-1.15.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:795c46999bae845966368a3c013e0e00947932d68e235702b5c3f6ea799aa8c9", size = 37244796 }, + { url = "https://files.pythonhosted.org/packages/10/c0/4f5f3eeccc235632aab79b27a74a9130c6c35df358129f7ac8b29f562ac7/scipy-1.15.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:18aaacb735ab38b38db42cb01f6b92a2d0d4b6aabefeb07f02849e47f8fb3594", size = 40047684 }, + { url = "https://files.pythonhosted.org/packages/ab/a7/0ddaf514ce8a8714f6ed243a2b391b41dbb65251affe21ee3077ec45ea9a/scipy-1.15.3-cp311-cp311-win_amd64.whl", hash = "sha256:ae48a786a28412d744c62fd7816a4118ef97e5be0bee968ce8f0a2fba7acf3bb", size = 41246504 }, + { url = "https://files.pythonhosted.org/packages/37/4b/683aa044c4162e10ed7a7ea30527f2cbd92e6999c10a8ed8edb253836e9c/scipy-1.15.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6ac6310fdbfb7aa6612408bd2f07295bcbd3fda00d2d702178434751fe48e019", size = 38766735 }, + { url = "https://files.pythonhosted.org/packages/7b/7e/f30be3d03de07f25dc0ec926d1681fed5c732d759ac8f51079708c79e680/scipy-1.15.3-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:185cd3d6d05ca4b44a8f1595af87f9c372bb6acf9c808e99aa3e9aa03bd98cf6", size = 30173284 }, + { url = "https://files.pythonhosted.org/packages/07/9c/0ddb0d0abdabe0d181c1793db51f02cd59e4901da6f9f7848e1f96759f0d/scipy-1.15.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:05dc6abcd105e1a29f95eada46d4a3f251743cfd7d3ae8ddb4088047f24ea477", size = 22446958 }, + { url = "https://files.pythonhosted.org/packages/af/43/0bce905a965f36c58ff80d8bea33f1f9351b05fad4beaad4eae34699b7a1/scipy-1.15.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:06efcba926324df1696931a57a176c80848ccd67ce6ad020c810736bfd58eb1c", size = 25242454 }, + { url = "https://files.pythonhosted.org/packages/56/30/a6f08f84ee5b7b28b4c597aca4cbe545535c39fe911845a96414700b64ba/scipy-1.15.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c05045d8b9bfd807ee1b9f38761993297b10b245f012b11b13b91ba8945f7e45", size = 35210199 }, + { url = "https://files.pythonhosted.org/packages/0b/1f/03f52c282437a168ee2c7c14a1a0d0781a9a4a8962d84ac05c06b4c5b555/scipy-1.15.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:271e3713e645149ea5ea3e97b57fdab61ce61333f97cfae392c28ba786f9bb49", size = 37309455 }, + { url = "https://files.pythonhosted.org/packages/89/b1/fbb53137f42c4bf630b1ffdfc2151a62d1d1b903b249f030d2b1c0280af8/scipy-1.15.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6cfd56fc1a8e53f6e89ba3a7a7251f7396412d655bca2aa5611c8ec9a6784a1e", size = 36885140 }, + { url = "https://files.pythonhosted.org/packages/2e/2e/025e39e339f5090df1ff266d021892694dbb7e63568edcfe43f892fa381d/scipy-1.15.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0ff17c0bb1cb32952c09217d8d1eed9b53d1463e5f1dd6052c7857f83127d539", size = 39710549 }, + { url = "https://files.pythonhosted.org/packages/e6/eb/3bf6ea8ab7f1503dca3a10df2e4b9c3f6b3316df07f6c0ded94b281c7101/scipy-1.15.3-cp312-cp312-win_amd64.whl", hash = "sha256:52092bc0472cfd17df49ff17e70624345efece4e1a12b23783a1ac59a1b728ed", size = 40966184 }, + { url = "https://files.pythonhosted.org/packages/73/18/ec27848c9baae6e0d6573eda6e01a602e5649ee72c27c3a8aad673ebecfd/scipy-1.15.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2c620736bcc334782e24d173c0fdbb7590a0a436d2fdf39310a8902505008759", size = 38728256 }, + { url = "https://files.pythonhosted.org/packages/74/cd/1aef2184948728b4b6e21267d53b3339762c285a46a274ebb7863c9e4742/scipy-1.15.3-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:7e11270a000969409d37ed399585ee530b9ef6aa99d50c019de4cb01e8e54e62", size = 30109540 }, + { url = "https://files.pythonhosted.org/packages/5b/d8/59e452c0a255ec352bd0a833537a3bc1bfb679944c4938ab375b0a6b3a3e/scipy-1.15.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:8c9ed3ba2c8a2ce098163a9bdb26f891746d02136995df25227a20e71c396ebb", size = 22383115 }, + { url = "https://files.pythonhosted.org/packages/08/f5/456f56bbbfccf696263b47095291040655e3cbaf05d063bdc7c7517f32ac/scipy-1.15.3-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:0bdd905264c0c9cfa74a4772cdb2070171790381a5c4d312c973382fc6eaf730", size = 25163884 }, + { url = "https://files.pythonhosted.org/packages/a2/66/a9618b6a435a0f0c0b8a6d0a2efb32d4ec5a85f023c2b79d39512040355b/scipy-1.15.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79167bba085c31f38603e11a267d862957cbb3ce018d8b38f79ac043bc92d825", size = 35174018 }, + { url = "https://files.pythonhosted.org/packages/b5/09/c5b6734a50ad4882432b6bb7c02baf757f5b2f256041da5df242e2d7e6b6/scipy-1.15.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9deabd6d547aee2c9a81dee6cc96c6d7e9a9b1953f74850c179f91fdc729cb7", size = 37269716 }, + { url = "https://files.pythonhosted.org/packages/77/0a/eac00ff741f23bcabd352731ed9b8995a0a60ef57f5fd788d611d43d69a1/scipy-1.15.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:dde4fc32993071ac0c7dd2d82569e544f0bdaff66269cb475e0f369adad13f11", size = 36872342 }, + { url = "https://files.pythonhosted.org/packages/fe/54/4379be86dd74b6ad81551689107360d9a3e18f24d20767a2d5b9253a3f0a/scipy-1.15.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f77f853d584e72e874d87357ad70f44b437331507d1c311457bed8ed2b956126", size = 39670869 }, + { url = "https://files.pythonhosted.org/packages/87/2e/892ad2862ba54f084ffe8cc4a22667eaf9c2bcec6d2bff1d15713c6c0703/scipy-1.15.3-cp313-cp313-win_amd64.whl", hash = "sha256:b90ab29d0c37ec9bf55424c064312930ca5f4bde15ee8619ee44e69319aab163", size = 40988851 }, + { url = "https://files.pythonhosted.org/packages/1b/e9/7a879c137f7e55b30d75d90ce3eb468197646bc7b443ac036ae3fe109055/scipy-1.15.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3ac07623267feb3ae308487c260ac684b32ea35fd81e12845039952f558047b8", size = 38863011 }, + { url = "https://files.pythonhosted.org/packages/51/d1/226a806bbd69f62ce5ef5f3ffadc35286e9fbc802f606a07eb83bf2359de/scipy-1.15.3-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:6487aa99c2a3d509a5227d9a5e889ff05830a06b2ce08ec30df6d79db5fcd5c5", size = 30266407 }, + { url = "https://files.pythonhosted.org/packages/e5/9b/f32d1d6093ab9eeabbd839b0f7619c62e46cc4b7b6dbf05b6e615bbd4400/scipy-1.15.3-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:50f9e62461c95d933d5c5ef4a1f2ebf9a2b4e83b0db374cb3f1de104d935922e", size = 22540030 }, + { url = "https://files.pythonhosted.org/packages/e7/29/c278f699b095c1a884f29fda126340fcc201461ee8bfea5c8bdb1c7c958b/scipy-1.15.3-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:14ed70039d182f411ffc74789a16df3835e05dc469b898233a245cdfd7f162cb", size = 25218709 }, + { url = "https://files.pythonhosted.org/packages/24/18/9e5374b617aba742a990581373cd6b68a2945d65cc588482749ef2e64467/scipy-1.15.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a769105537aa07a69468a0eefcd121be52006db61cdd8cac8a0e68980bbb723", size = 34809045 }, + { url = "https://files.pythonhosted.org/packages/e1/fe/9c4361e7ba2927074360856db6135ef4904d505e9b3afbbcb073c4008328/scipy-1.15.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9db984639887e3dffb3928d118145ffe40eff2fa40cb241a306ec57c219ebbbb", size = 36703062 }, + { url = "https://files.pythonhosted.org/packages/b7/8e/038ccfe29d272b30086b25a4960f757f97122cb2ec42e62b460d02fe98e9/scipy-1.15.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:40e54d5c7e7ebf1aa596c374c49fa3135f04648a0caabcb66c52884b943f02b4", size = 36393132 }, + { url = "https://files.pythonhosted.org/packages/10/7e/5c12285452970be5bdbe8352c619250b97ebf7917d7a9a9e96b8a8140f17/scipy-1.15.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:5e721fed53187e71d0ccf382b6bf977644c533e506c4d33c3fb24de89f5c3ed5", size = 38979503 }, + { url = "https://files.pythonhosted.org/packages/81/06/0a5e5349474e1cbc5757975b21bd4fad0e72ebf138c5592f191646154e06/scipy-1.15.3-cp313-cp313t-win_amd64.whl", hash = "sha256:76ad1fb5f8752eabf0fa02e4cc0336b4e8f021e2d5f061ed37d6d264db35e3ca", size = 40308097 }, +] + +[[package]] +name = "sentry-sdk" +version = "2.29.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/22/67/d552a5f8e5a6a56b2feea6529e2d8ccd54349084c84176d5a1f7295044bc/sentry_sdk-2.29.1.tar.gz", hash = "sha256:8d4a0206b95fa5fe85e5e7517ed662e3888374bdc342c00e435e10e6d831aa6d", size = 325518 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f0/e5/da07b0bd832cefd52d16f2b9bbbe31624d57552602c06631686b93ccb1bd/sentry_sdk-2.29.1-py2.py3-none-any.whl", hash = "sha256:90862fe0616ded4572da6c9dadb363121a1ae49a49e21c418f0634e9d10b4c19", size = 341553 }, +] + +[[package]] +name = "setproctitle" +version = "1.3.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9e/af/56efe21c53ac81ac87e000b15e60b3d8104224b4313b6eacac3597bd183d/setproctitle-1.3.6.tar.gz", hash = "sha256:c9f32b96c700bb384f33f7cf07954bb609d35dd82752cef57fb2ee0968409169", size = 26889 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/27/3b/8288d0cd969a63500dd62fc2c99ce6980f9909ccef0770ab1f86c361e0bf/setproctitle-1.3.6-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a1d856b0f4e4a33e31cdab5f50d0a14998f3a2d726a3fd5cb7c4d45a57b28d1b", size = 17412 }, + { url = "https://files.pythonhosted.org/packages/39/37/43a5a3e25ca1048dbbf4db0d88d346226f5f1acd131bb8e660f4bfe2799f/setproctitle-1.3.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:50706b9c0eda55f7de18695bfeead5f28b58aa42fd5219b3b1692d554ecbc9ec", size = 11963 }, + { url = "https://files.pythonhosted.org/packages/5b/47/f103c40e133154783c91a10ab08ac9fc410ed835aa85bcf7107cb882f505/setproctitle-1.3.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:af188f3305f0a65c3217c30c6d4c06891e79144076a91e8b454f14256acc7279", size = 31718 }, + { url = "https://files.pythonhosted.org/packages/1f/13/7325dd1c008dd6c0ebd370ddb7505977054a87e406f142318e395031a792/setproctitle-1.3.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cce0ed8b3f64c71c140f0ec244e5fdf8ecf78ddf8d2e591d4a8b6aa1c1214235", size = 33027 }, + { url = "https://files.pythonhosted.org/packages/0c/0a/6075bfea05a71379d77af98a9ac61163e8b6e5ef1ae58cd2b05871b2079c/setproctitle-1.3.6-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:70100e2087fe05359f249a0b5f393127b3a1819bf34dec3a3e0d4941138650c9", size = 30223 }, + { url = "https://files.pythonhosted.org/packages/cc/41/fbf57ec52f4f0776193bd94334a841f0bc9d17e745f89c7790f336420c65/setproctitle-1.3.6-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1065ed36bd03a3fd4186d6c6de5f19846650b015789f72e2dea2d77be99bdca1", size = 31204 }, + { url = "https://files.pythonhosted.org/packages/97/b5/f799fb7a00de29fb0ac1dfd015528dea425b9e31a8f1068a0b3df52d317f/setproctitle-1.3.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4adf6a0013fe4e0844e3ba7583ec203ca518b9394c6cc0d3354df2bf31d1c034", size = 31181 }, + { url = "https://files.pythonhosted.org/packages/b5/b7/81f101b612014ec61723436022c31146178813d6ca6b947f7b9c84e9daf4/setproctitle-1.3.6-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:eb7452849f6615871eabed6560ffedfe56bc8af31a823b6be4ce1e6ff0ab72c5", size = 30101 }, + { url = "https://files.pythonhosted.org/packages/67/23/681232eed7640eab96719daa8647cc99b639e3daff5c287bd270ef179a73/setproctitle-1.3.6-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a094b7ce455ca341b59a0f6ce6be2e11411ba6e2860b9aa3dbb37468f23338f4", size = 32438 }, + { url = "https://files.pythonhosted.org/packages/19/f8/4d075a7bdc3609ac71535b849775812455e4c40aedfbf0778a6f123b1774/setproctitle-1.3.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ad1c2c2baaba62823a7f348f469a967ece0062140ca39e7a48e4bbb1f20d54c4", size = 30625 }, + { url = "https://files.pythonhosted.org/packages/5f/73/a2a8259ebee166aee1ca53eead75de0e190b3ddca4f716e5c7470ebb7ef6/setproctitle-1.3.6-cp311-cp311-win32.whl", hash = "sha256:8050c01331135f77ec99d99307bfbc6519ea24d2f92964b06f3222a804a3ff1f", size = 11488 }, + { url = "https://files.pythonhosted.org/packages/c9/15/52cf5e1ff0727d53704cfdde2858eaf237ce523b0b04db65faa84ff83e13/setproctitle-1.3.6-cp311-cp311-win_amd64.whl", hash = "sha256:9b73cf0fe28009a04a35bb2522e4c5b5176cc148919431dcb73fdbdfaab15781", size = 12201 }, + { url = "https://files.pythonhosted.org/packages/8f/fb/99456fd94d4207c5f6c40746a048a33a52b4239cd7d9c8d4889e2210ec82/setproctitle-1.3.6-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:af44bb7a1af163806bbb679eb8432fa7b4fb6d83a5d403b541b675dcd3798638", size = 17399 }, + { url = "https://files.pythonhosted.org/packages/d5/48/9699191fe6062827683c43bfa9caac33a2c89f8781dd8c7253fa3dba85fd/setproctitle-1.3.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3cca16fd055316a48f0debfcbfb6af7cea715429fc31515ab3fcac05abd527d8", size = 11966 }, + { url = "https://files.pythonhosted.org/packages/33/03/b085d192b9ecb9c7ce6ad6ef30ecf4110b7f39430b58a56245569827fcf4/setproctitle-1.3.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea002088d5554fd75e619742cefc78b84a212ba21632e59931b3501f0cfc8f67", size = 32017 }, + { url = "https://files.pythonhosted.org/packages/ae/68/c53162e645816f97212002111420d1b2f75bf6d02632e37e961dc2cd6d8b/setproctitle-1.3.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bb465dd5825356c1191a038a86ee1b8166e3562d6e8add95eec04ab484cfb8a2", size = 33419 }, + { url = "https://files.pythonhosted.org/packages/ac/0d/119a45d15a816a6cf5ccc61b19729f82620095b27a47e0a6838216a95fae/setproctitle-1.3.6-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d2c8e20487b3b73c1fa72c56f5c89430617296cd380373e7af3a538a82d4cd6d", size = 30711 }, + { url = "https://files.pythonhosted.org/packages/e3/fb/5e9b5068df9e9f31a722a775a5e8322a29a638eaaa3eac5ea7f0b35e6314/setproctitle-1.3.6-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0d6252098e98129a1decb59b46920d4eca17b0395f3d71b0d327d086fefe77d", size = 31742 }, + { url = "https://files.pythonhosted.org/packages/35/88/54de1e73e8fce87d587889c7eedb48fc4ee2bbe4e4ca6331690d03024f86/setproctitle-1.3.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:cf355fbf0d4275d86f9f57be705d8e5eaa7f8ddb12b24ced2ea6cbd68fdb14dc", size = 31925 }, + { url = "https://files.pythonhosted.org/packages/f3/01/65948d7badd66e63e3db247b923143da142790fa293830fdecf832712c2d/setproctitle-1.3.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e288f8a162d663916060beb5e8165a8551312b08efee9cf68302687471a6545d", size = 30981 }, + { url = "https://files.pythonhosted.org/packages/22/20/c495e61786f1d38d5dc340b9d9077fee9be3dfc7e89f515afe12e1526dbc/setproctitle-1.3.6-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:b2e54f4a2dc6edf0f5ea5b1d0a608d2af3dcb5aa8c8eeab9c8841b23e1b054fe", size = 33209 }, + { url = "https://files.pythonhosted.org/packages/98/3f/a457b8550fbd34d5b482fe20b8376b529e76bf1fbf9a474a6d9a641ab4ad/setproctitle-1.3.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b6f4abde9a2946f57e8daaf1160b2351bcf64274ef539e6675c1d945dbd75e2a", size = 31587 }, + { url = "https://files.pythonhosted.org/packages/44/fe/743517340e5a635e3f1c4310baea20c16c66202f96a6f4cead222ffd6d84/setproctitle-1.3.6-cp312-cp312-win32.whl", hash = "sha256:db608db98ccc21248370d30044a60843b3f0f3d34781ceeea67067c508cd5a28", size = 11487 }, + { url = "https://files.pythonhosted.org/packages/60/9a/d88f1c1f0f4efff1bd29d9233583ee341114dda7d9613941453984849674/setproctitle-1.3.6-cp312-cp312-win_amd64.whl", hash = "sha256:082413db8a96b1f021088e8ec23f0a61fec352e649aba20881895815388b66d3", size = 12208 }, + { url = "https://files.pythonhosted.org/packages/89/76/f1a2fdbf9b9602945a7489ba5c52e9863de37381ef1a85a2b9ed0ff8bc79/setproctitle-1.3.6-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e2a9e62647dc040a76d55563580bf3bb8fe1f5b6ead08447c2ed0d7786e5e794", size = 17392 }, + { url = "https://files.pythonhosted.org/packages/5c/5b/4e0db8b10b4543afcb3dbc0827793d46e43ec1de6b377e313af3703d08e0/setproctitle-1.3.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:751ba352ed922e0af60458e961167fa7b732ac31c0ddd1476a2dfd30ab5958c5", size = 11951 }, + { url = "https://files.pythonhosted.org/packages/dc/fe/d5d00aaa700fe1f6160b6e95c225b29c01f4d9292176d48fd968815163ea/setproctitle-1.3.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7890e291bf4708e3b61db9069ea39b3ab0651e42923a5e1f4d78a7b9e4b18301", size = 32087 }, + { url = "https://files.pythonhosted.org/packages/9f/b3/894b827b93ef813c082479bebf88185860f01ac243df737823dd705e7fff/setproctitle-1.3.6-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b2b17855ed7f994f3f259cf2dfbfad78814538536fa1a91b50253d84d87fd88d", size = 33502 }, + { url = "https://files.pythonhosted.org/packages/b2/cd/5330734cca1a4cfcb721432c22cb7899ff15a4101ba868b2ef452ffafea1/setproctitle-1.3.6-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e51ec673513465663008ce402171192a053564865c2fc6dc840620871a9bd7c", size = 30713 }, + { url = "https://files.pythonhosted.org/packages/fa/d3/c2590c5daa2e9a008d3f2b16c0f4a351826193be55f147cb32af49c6d814/setproctitle-1.3.6-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63cc10352dc6cf35a33951656aa660d99f25f574eb78132ce41a85001a638aa7", size = 31792 }, + { url = "https://files.pythonhosted.org/packages/e6/b1/c553ed5af8cfcecd5ae7737e63af58a17a03d26f3d61868c7eb20bf7e3cf/setproctitle-1.3.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0dba8faee2e4a96e934797c9f0f2d093f8239bf210406a99060b3eabe549628e", size = 31927 }, + { url = "https://files.pythonhosted.org/packages/70/78/2d5385206540127a3dca0ff83225b1ac66873f5cc89d4a6d3806c92f5ae2/setproctitle-1.3.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:e3e44d08b61de0dd6f205528498f834a51a5c06689f8fb182fe26f3a3ce7dca9", size = 30981 }, + { url = "https://files.pythonhosted.org/packages/31/62/e3e4a4e006d0e549748e53cded4ff3b667be0602860fc61b7de8b412b667/setproctitle-1.3.6-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:de004939fc3fd0c1200d26ea9264350bfe501ffbf46c8cf5dc7f345f2d87a7f1", size = 33244 }, + { url = "https://files.pythonhosted.org/packages/aa/05/4b223fd4ef94e105dc7aff27fa502fb7200cf52be2bb0c064bd2406b5611/setproctitle-1.3.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3f8194b4d631b003a1176a75d1acd545e04b1f54b821638e098a93e6e62830ef", size = 31630 }, + { url = "https://files.pythonhosted.org/packages/1b/ba/5f68eb969f7336f54b54a599fd3ffbd7662f9733b080bc8598705971b3dd/setproctitle-1.3.6-cp313-cp313-win32.whl", hash = "sha256:d714e002dd3638170fe7376dc1b686dbac9cb712cde3f7224440af722cc9866a", size = 11480 }, + { url = "https://files.pythonhosted.org/packages/ba/f5/7f47f0ca35c9c357f16187cee9229f3eda0237bc6fdd3061441336f361c0/setproctitle-1.3.6-cp313-cp313-win_amd64.whl", hash = "sha256:b70c07409d465f3a8b34d52f863871fb8a00755370791d2bd1d4f82b3cdaf3d5", size = 12198 }, + { url = "https://files.pythonhosted.org/packages/39/ad/c3941b8fc6b32a976c9e2d9615a90ae793b69cd010ca8c3575dbc822104f/setproctitle-1.3.6-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:23a57d3b8f1549515c2dbe4a2880ebc1f27780dc126c5e064167563e015817f5", size = 17401 }, + { url = "https://files.pythonhosted.org/packages/04/38/a184f857b988d3a9c401e470a4e38182a5c99ee77bf90432d7665e9d35a3/setproctitle-1.3.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:81c443310831e29fabbd07b75ebbfa29d0740b56f5907c6af218482d51260431", size = 11959 }, + { url = "https://files.pythonhosted.org/packages/b7/b9/4878ef9d8483adfd1edf6bf95151362aaec0d05aac306a97ff0383f491b5/setproctitle-1.3.6-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d88c63bd395c787b0aa81d8bbc22c1809f311032ce3e823a6517b711129818e4", size = 33463 }, + { url = "https://files.pythonhosted.org/packages/cc/60/3ef49d1931aff2a36a7324a49cca10d77ef03e0278452fd468c33a52d7e3/setproctitle-1.3.6-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d73f14b86d0e2858ece6bf5807c9889670e392c001d414b4293d0d9b291942c3", size = 34959 }, + { url = "https://files.pythonhosted.org/packages/81/c6/dee0a973acecefb0db6c9c2e0ea7f18b7e4db773a72e534741ebdee8bbb8/setproctitle-1.3.6-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3393859eb8f19f5804049a685bf286cb08d447e28ba5c6d8543c7bf5500d5970", size = 32055 }, + { url = "https://files.pythonhosted.org/packages/ea/a5/5dd5c4192cf18d16349a32a07f728a9a48a2a05178e16966cabd6645903e/setproctitle-1.3.6-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:785cd210c0311d9be28a70e281a914486d62bfd44ac926fcd70cf0b4d65dff1c", size = 32986 }, + { url = "https://files.pythonhosted.org/packages/df/a6/1508d37eb8008670d33f13fcdb91cbd8ef54697276469abbfdd3d4428c59/setproctitle-1.3.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c051f46ed1e13ba8214b334cbf21902102807582fbfaf0fef341b9e52f0fafbf", size = 32736 }, + { url = "https://files.pythonhosted.org/packages/1a/73/c84ec8880d543766a12fcd6b65dbd013770974a40577889f357409b0441e/setproctitle-1.3.6-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:49498ebf68ca3e75321ffe634fcea5cc720502bfaa79bd6b03ded92ce0dc3c24", size = 31945 }, + { url = "https://files.pythonhosted.org/packages/95/0a/126b9ff7a406a69a62825fe5bd6d1ba8671919a7018c4f9e2c63f49bfcb6/setproctitle-1.3.6-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:4431629c178193f23c538cb1de3da285a99ccc86b20ee91d81eb5f1a80e0d2ba", size = 34333 }, + { url = "https://files.pythonhosted.org/packages/9a/fd/5474b04f1c013ff460129d2bc774557dd6e186da4667865efef9a83bf378/setproctitle-1.3.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d136fbf8ad4321716e44d6d6b3d8dffb4872626010884e07a1db54b7450836cf", size = 32508 }, + { url = "https://files.pythonhosted.org/packages/32/21/2503e38520cb076a7ecaef6a35d6a6fa89cf02af3541c84c811fd7500d20/setproctitle-1.3.6-cp313-cp313t-win32.whl", hash = "sha256:d483cc23cc56ab32911ea0baa0d2d9ea7aa065987f47de847a0a93a58bf57905", size = 11482 }, + { url = "https://files.pythonhosted.org/packages/65/23/7833d75a27fba25ddc5cd3b54cd03c4bf8e18b8e2dbec622eb6326278ce8/setproctitle-1.3.6-cp313-cp313t-win_amd64.whl", hash = "sha256:74973aebea3543ad033b9103db30579ec2b950a466e09f9c2180089e8346e0ec", size = 12209 }, +] + +[[package]] +name = "setuptools" +version = "80.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/18/5d/3bf57dcd21979b887f014ea83c24ae194cfcd12b9e0fda66b957c69d1fca/setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c", size = 1319958 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772/setuptools-80.9.0-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922", size = 1201486 }, +] + +[[package]] +name = "shapely" +version = "2.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ca/3c/2da625233f4e605155926566c0e7ea8dda361877f48e8b1655e53456f252/shapely-2.1.1.tar.gz", hash = "sha256:500621967f2ffe9642454808009044c21e5b35db89ce69f8a2042c2ffd0e2772", size = 315422 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/19/97/2df985b1e03f90c503796ad5ecd3d9ed305123b64d4ccb54616b30295b29/shapely-2.1.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:587a1aa72bc858fab9b8c20427b5f6027b7cbc92743b8e2c73b9de55aa71c7a7", size = 1819368 }, + { url = "https://files.pythonhosted.org/packages/56/17/504518860370f0a28908b18864f43d72f03581e2b6680540ca668f07aa42/shapely-2.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9fa5c53b0791a4b998f9ad84aad456c988600757a96b0a05e14bba10cebaaaea", size = 1625362 }, + { url = "https://files.pythonhosted.org/packages/36/a1/9677337d729b79fce1ef3296aac6b8ef4743419086f669e8a8070eff8f40/shapely-2.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aabecd038841ab5310d23495253f01c2a82a3aedae5ab9ca489be214aa458aa7", size = 2999005 }, + { url = "https://files.pythonhosted.org/packages/a2/17/e09357274699c6e012bbb5a8ea14765a4d5860bb658df1931c9f90d53bd3/shapely-2.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:586f6aee1edec04e16227517a866df3e9a2e43c1f635efc32978bb3dc9c63753", size = 3108489 }, + { url = "https://files.pythonhosted.org/packages/17/5d/93a6c37c4b4e9955ad40834f42b17260ca74ecf36df2e81bb14d12221b90/shapely-2.1.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b9878b9e37ad26c72aada8de0c9cfe418d9e2ff36992a1693b7f65a075b28647", size = 3945727 }, + { url = "https://files.pythonhosted.org/packages/a3/1a/ad696648f16fd82dd6bfcca0b3b8fbafa7aacc13431c7fc4c9b49e481681/shapely-2.1.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d9a531c48f289ba355e37b134e98e28c557ff13965d4653a5228d0f42a09aed0", size = 4109311 }, + { url = "https://files.pythonhosted.org/packages/d4/38/150dd245beab179ec0d4472bf6799bf18f21b1efbef59ac87de3377dbf1c/shapely-2.1.1-cp311-cp311-win32.whl", hash = "sha256:4866de2673a971820c75c0167b1f1cd8fb76f2d641101c23d3ca021ad0449bab", size = 1522982 }, + { url = "https://files.pythonhosted.org/packages/93/5b/842022c00fbb051083c1c85430f3bb55565b7fd2d775f4f398c0ba8052ce/shapely-2.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:20a9d79958b3d6c70d8a886b250047ea32ff40489d7abb47d01498c704557a93", size = 1703872 }, + { url = "https://files.pythonhosted.org/packages/fb/64/9544dc07dfe80a2d489060791300827c941c451e2910f7364b19607ea352/shapely-2.1.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2827365b58bf98efb60affc94a8e01c56dd1995a80aabe4b701465d86dcbba43", size = 1833021 }, + { url = "https://files.pythonhosted.org/packages/07/aa/fb5f545e72e89b6a0f04a0effda144f5be956c9c312c7d4e00dfddbddbcf/shapely-2.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a9c551f7fa7f1e917af2347fe983f21f212863f1d04f08eece01e9c275903fad", size = 1643018 }, + { url = "https://files.pythonhosted.org/packages/03/46/61e03edba81de729f09d880ce7ae5c1af873a0814206bbfb4402ab5c3388/shapely-2.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78dec4d4fbe7b1db8dc36de3031767e7ece5911fb7782bc9e95c5cdec58fb1e9", size = 2986417 }, + { url = "https://files.pythonhosted.org/packages/1f/1e/83ec268ab8254a446b4178b45616ab5822d7b9d2b7eb6e27cf0b82f45601/shapely-2.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:872d3c0a7b8b37da0e23d80496ec5973c4692920b90de9f502b5beb994bbaaef", size = 3098224 }, + { url = "https://files.pythonhosted.org/packages/f1/44/0c21e7717c243e067c9ef8fa9126de24239f8345a5bba9280f7bb9935959/shapely-2.1.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2e2b9125ebfbc28ecf5353511de62f75a8515ae9470521c9a693e4bb9fbe0cf1", size = 3925982 }, + { url = "https://files.pythonhosted.org/packages/15/50/d3b4e15fefc103a0eb13d83bad5f65cd6e07a5d8b2ae920e767932a247d1/shapely-2.1.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4b96cea171b3d7f6786976a0520f178c42792897653ecca0c5422fb1e6946e6d", size = 4089122 }, + { url = "https://files.pythonhosted.org/packages/bd/05/9a68f27fc6110baeedeeebc14fd86e73fa38738c5b741302408fb6355577/shapely-2.1.1-cp312-cp312-win32.whl", hash = "sha256:39dca52201e02996df02e447f729da97cfb6ff41a03cb50f5547f19d02905af8", size = 1522437 }, + { url = "https://files.pythonhosted.org/packages/bc/e9/a4560e12b9338842a1f82c9016d2543eaa084fce30a1ca11991143086b57/shapely-2.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:13d643256f81d55a50013eff6321142781cf777eb6a9e207c2c9e6315ba6044a", size = 1703479 }, + { url = "https://files.pythonhosted.org/packages/71/8e/2bc836437f4b84d62efc1faddce0d4e023a5d990bbddd3c78b2004ebc246/shapely-2.1.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3004a644d9e89e26c20286d5fdc10f41b1744c48ce910bd1867fdff963fe6c48", size = 1832107 }, + { url = "https://files.pythonhosted.org/packages/12/a2/12c7cae5b62d5d851c2db836eadd0986f63918a91976495861f7c492f4a9/shapely-2.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1415146fa12d80a47d13cfad5310b3c8b9c2aa8c14a0c845c9d3d75e77cb54f6", size = 1642355 }, + { url = "https://files.pythonhosted.org/packages/5b/7e/6d28b43d53fea56de69c744e34c2b999ed4042f7a811dc1bceb876071c95/shapely-2.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21fcab88b7520820ec16d09d6bea68652ca13993c84dffc6129dc3607c95594c", size = 2968871 }, + { url = "https://files.pythonhosted.org/packages/dd/87/1017c31e52370b2b79e4d29e07cbb590ab9e5e58cf7e2bdfe363765d6251/shapely-2.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e5ce6a5cc52c974b291237a96c08c5592e50f066871704fb5b12be2639d9026a", size = 3080830 }, + { url = "https://files.pythonhosted.org/packages/1d/fe/f4a03d81abd96a6ce31c49cd8aaba970eaaa98e191bd1e4d43041e57ae5a/shapely-2.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:04e4c12a45a1d70aeb266618d8cf81a2de9c4df511b63e105b90bfdfb52146de", size = 3908961 }, + { url = "https://files.pythonhosted.org/packages/ef/59/7605289a95a6844056a2017ab36d9b0cb9d6a3c3b5317c1f968c193031c9/shapely-2.1.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6ca74d851ca5264aae16c2b47e96735579686cb69fa93c4078070a0ec845b8d8", size = 4079623 }, + { url = "https://files.pythonhosted.org/packages/bc/4d/9fea036eff2ef4059d30247128b2d67aaa5f0b25e9fc27e1d15cc1b84704/shapely-2.1.1-cp313-cp313-win32.whl", hash = "sha256:fd9130501bf42ffb7e0695b9ea17a27ae8ce68d50b56b6941c7f9b3d3453bc52", size = 1521916 }, + { url = "https://files.pythonhosted.org/packages/12/d9/6d13b8957a17c95794f0c4dfb65ecd0957e6c7131a56ce18d135c1107a52/shapely-2.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:ab8d878687b438a2f4c138ed1a80941c6ab0029e0f4c785ecfe114413b498a97", size = 1702746 }, + { url = "https://files.pythonhosted.org/packages/60/36/b1452e3e7f35f5f6454d96f3be6e2bb87082720ff6c9437ecc215fa79be0/shapely-2.1.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0c062384316a47f776305ed2fa22182717508ffdeb4a56d0ff4087a77b2a0f6d", size = 1833482 }, + { url = "https://files.pythonhosted.org/packages/ce/ca/8e6f59be0718893eb3e478141285796a923636dc8f086f83e5b0ec0036d0/shapely-2.1.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4ecf6c196b896e8f1360cc219ed4eee1c1e5f5883e505d449f263bd053fb8c05", size = 1642256 }, + { url = "https://files.pythonhosted.org/packages/ab/78/0053aea449bb1d4503999525fec6232f049abcdc8df60d290416110de943/shapely-2.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb00070b4c4860f6743c600285109c273cca5241e970ad56bb87bef0be1ea3a0", size = 3016614 }, + { url = "https://files.pythonhosted.org/packages/ee/53/36f1b1de1dfafd1b457dcbafa785b298ce1b8a3e7026b79619e708a245d5/shapely-2.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d14a9afa5fa980fbe7bf63706fdfb8ff588f638f145a1d9dbc18374b5b7de913", size = 3093542 }, + { url = "https://files.pythonhosted.org/packages/b9/bf/0619f37ceec6b924d84427c88835b61f27f43560239936ff88915c37da19/shapely-2.1.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b640e390dabde790e3fb947198b466e63223e0a9ccd787da5f07bcb14756c28d", size = 3945961 }, + { url = "https://files.pythonhosted.org/packages/93/c9/20ca4afeb572763b07a7997f00854cb9499df6af85929e93012b189d8917/shapely-2.1.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:69e08bf9697c1b73ec6aa70437db922bafcea7baca131c90c26d59491a9760f9", size = 4089514 }, + { url = "https://files.pythonhosted.org/packages/33/6a/27036a5a560b80012a544366bceafd491e8abb94a8db14047b5346b5a749/shapely-2.1.1-cp313-cp313t-win32.whl", hash = "sha256:ef2d09d5a964cc90c2c18b03566cf918a61c248596998a0301d5b632beadb9db", size = 1540607 }, + { url = "https://files.pythonhosted.org/packages/ea/f1/5e9b3ba5c7aa7ebfaf269657e728067d16a7c99401c7973ddf5f0cf121bd/shapely-2.1.1-cp313-cp313t-win_amd64.whl", hash = "sha256:8cb8f17c377260452e9d7720eeaf59082c5f8ea48cf104524d953e5d36d4bdb7", size = 1723061 }, +] + +[[package]] +name = "six" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050 }, +] + +[[package]] +name = "smmap" +version = "5.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/44/cd/a040c4b3119bbe532e5b0732286f805445375489fceaec1f48306068ee3b/smmap-5.0.2.tar.gz", hash = "sha256:26ea65a03958fa0c8a1c7e8c7a58fdc77221b8910f6be2131affade476898ad5", size = 22329 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/be/d09147ad1ec7934636ad912901c5fd7667e1c858e19d355237db0d0cd5e4/smmap-5.0.2-py3-none-any.whl", hash = "sha256:b30115f0def7d7531d22a0fb6502488d879e75b260a9db4d0819cfb25403af5e", size = 24303 }, +] + +[[package]] +name = "soupsieve" +version = "2.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3f/f4/4a80cd6ef364b2e8b65b15816a843c0980f7a5a2b4dc701fc574952aa19f/soupsieve-2.7.tar.gz", hash = "sha256:ad282f9b6926286d2ead4750552c8a6142bc4c783fd66b0293547c8fe6ae126a", size = 103418 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/9c/0e6afc12c269578be5c0c1c9f4b49a8d32770a080260c333ac04cc1c832d/soupsieve-2.7-py3-none-any.whl", hash = "sha256:6e60cc5c1ffaf1cebcc12e8188320b72071e922c2e897f737cadce79ad5d30c4", size = 36677 }, +] + +[[package]] +name = "stack-data" +version = "0.6.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "asttokens" }, + { name = "executing" }, + { name = "pure-eval" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/28/e3/55dcc2cfbc3ca9c29519eb6884dd1415ecb53b0e934862d3559ddcb7e20b/stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9", size = 44707 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695", size = 24521 }, +] + +[[package]] +name = "stamp" +version = "2.1.0" +source = { editable = "." } +dependencies = [ + { name = "beartype" }, + { name = "einops" }, + { name = "h5py" }, + { name = "jaxtyping" }, + { name = "lightning" }, + { name = "matplotlib" }, + { name = "numpy" }, + { name = "opencv-python" }, + { name = "openpyxl" }, + { name = "openslide-bin" }, + { name = "openslide-python" }, + { name = "packaging" }, + { name = "pandas" }, + { name = "pillow" }, + { name = "pydantic" }, + { name = "pyyaml" }, + { name = "scikit-learn" }, + { name = "scipy" }, + { name = "timm" }, + { name = "torch" }, + { name = "torchmetrics" }, + { name = "torchvision" }, + { name = "tqdm" }, +] + +[package.optional-dependencies] +all = [ + { name = "conch" }, + { name = "einops-exts" }, + { name = "environs" }, + { name = "gdown" }, + { name = "gigapath" }, + { name = "huggingface-hub" }, + { name = "madeleine" }, + { name = "musk" }, + { name = "sacremoses" }, + { name = "torch" }, + { name = "transformers" }, + { name = "uni" }, +] +chief-ctranspath = [ + { name = "gdown" }, + { name = "torch" }, +] +cobra = [ + { name = "cobra" }, + { name = "jinja2" }, +] +conch = [ + { name = "conch" }, + { name = "huggingface-hub" }, +] +conch1-5 = [ + { name = "einops-exts" }, + { name = "transformers" }, +] +ctranspath = [ + { name = "gdown" }, +] +gigapath = [ + { name = "gigapath" }, +] +madeleine = [ + { name = "madeleine" }, +] +musk = [ + { name = "musk" }, +] +plip = [ + { name = "transformers" }, +] +prism = [ + { name = "environs" }, + { name = "sacremoses" }, +] +uni = [ + { name = "huggingface-hub" }, + { name = "uni" }, +] +virchow2 = [ + { name = "huggingface-hub" }, + { name = "torch" }, +] + +[package.dev-dependencies] +dev = [ + { name = "huggingface-hub" }, + { name = "ipykernel" }, + { name = "pyright" }, + { name = "pytest" }, + { name = "ruff" }, +] + +[package.metadata] +requires-dist = [ + { name = "beartype", specifier = ">=0.19.0" }, + { name = "cobra", marker = "extra == 'cobra'", git = "https://github.com/KatherLab/COBRA.git?rev=f1a576e1133330ffc2d1df6ee110701921c7b7c9#f1a576e1133330ffc2d1df6ee110701921c7b7c9" }, + { name = "conch", marker = "extra == 'conch'", git = "https://github.com/Mahmoodlab/CONCH.git?rev=02d6ac59cc20874bff0f581de258c2b257f69a84#02d6ac59cc20874bff0f581de258c2b257f69a84" }, + { name = "einops", specifier = ">=0.8.0" }, + { name = "einops-exts", marker = "extra == 'conch1-5'", specifier = "==0.0.4" }, + { name = "environs", marker = "extra == 'prism'", specifier = "==11.0.0" }, + { name = "gdown", marker = "extra == 'chief-ctranspath'", specifier = ">=5.2.0" }, + { name = "gdown", marker = "extra == 'ctranspath'", specifier = ">=5.2.0" }, + { name = "gigapath", marker = "extra == 'gigapath'", git = "https://github.com/EzicStar/prov-gigapath.git?rev=d4cf55321df37aaf867e24a31c61bcf490a296eb#d4cf55321df37aaf867e24a31c61bcf490a296eb" }, + { name = "h5py", specifier = ">=3.12.1" }, + { name = "huggingface-hub", marker = "extra == 'conch'", specifier = ">=0.26.2" }, + { name = "huggingface-hub", marker = "extra == 'uni'", specifier = ">=0.26.2" }, + { name = "huggingface-hub", marker = "extra == 'virchow2'", specifier = ">=0.27.1" }, + { name = "jaxtyping", specifier = ">=0.2.36" }, + { name = "jinja2", marker = "extra == 'cobra'", specifier = ">=3.1.4" }, + { name = "lightning", specifier = ">=2.4.0" }, + { name = "madeleine", marker = "extra == 'madeleine'", git = "https://github.com/mahmoodlab/MADELEINE.git?rev=de7c85acc2bdad352e6df8eee5694f8b6f288012#de7c85acc2bdad352e6df8eee5694f8b6f288012" }, + { name = "matplotlib", specifier = ">=3.9.2" }, + { name = "musk", marker = "extra == 'musk'", git = "https://github.com/lilab-stanford/MUSK.git?rev=e1699c27687f44bbf6d4adfcbb2abe89795d347f#e1699c27687f44bbf6d4adfcbb2abe89795d347f" }, + { name = "numpy", specifier = ">=2.2.2" }, + { name = "opencv-python", specifier = ">=4.10.0.84" }, + { name = "openpyxl", specifier = ">=3.1.5" }, + { name = "openslide-bin", specifier = ">=4.0.0.6" }, + { name = "openslide-python", specifier = ">=1.4.1" }, + { name = "packaging", specifier = ">=24.2" }, + { name = "pandas", specifier = ">=2.2.3" }, + { name = "pillow", specifier = ">=11.1.0" }, + { name = "pydantic", specifier = ">=2.10.3" }, + { name = "pyyaml", specifier = ">=6.0.2" }, + { name = "sacremoses", marker = "extra == 'prism'", specifier = "==0.1.1" }, + { name = "scikit-learn", specifier = ">=1.5.2" }, + { name = "scipy", specifier = ">=1.15.1" }, + { name = "stamp", extras = ["conch", "ctranspath", "uni", "virchow2", "chief-ctranspath", "conch1-5", "gigapath", "prism", "madeleine", "musk", "plip"], marker = "extra == 'all'" }, + { name = "timm", specifier = ">=0.9.11" }, + { name = "torch", specifier = ">=2.5.1" }, + { name = "torch", marker = "extra == 'chief-ctranspath'", specifier = ">=2.0.0" }, + { name = "torch", marker = "extra == 'virchow2'", specifier = ">=2.0.0" }, + { name = "torchmetrics", specifier = ">=1.6.0" }, + { name = "torchvision", specifier = ">=0.20.1" }, + { name = "tqdm", specifier = ">=4.66.6" }, + { name = "transformers", marker = "extra == 'conch1-5'", specifier = ">=4.45.2" }, + { name = "transformers", marker = "extra == 'plip'", specifier = ">=4.45.2" }, + { name = "uni", marker = "extra == 'uni'", git = "https://github.com/mahmoodlab/UNI.git" }, +] + +[package.metadata.requires-dev] +dev = [ + { name = "huggingface-hub", specifier = ">=0.27.1" }, + { name = "ipykernel", specifier = ">=6.29.5" }, + { name = "pyright", specifier = ">=1.1.389,!=1.1.391" }, + { name = "pytest", specifier = ">=8.3.4" }, + { name = "ruff", specifier = ">=0.8.1" }, +] + +[[package]] +name = "sympy" +version = "1.13.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mpmath" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ca/99/5a5b6f19ff9f083671ddf7b9632028436167cd3d33e11015754e41b249a4/sympy-1.13.1.tar.gz", hash = "sha256:9cebf7e04ff162015ce31c9c6c9144daa34a93bd082f54fd8f12deca4f47515f", size = 7533040 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b2/fe/81695a1aa331a842b582453b605175f419fe8540355886031328089d840a/sympy-1.13.1-py3-none-any.whl", hash = "sha256:db36cdc64bf61b9b24578b6f7bab1ecdd2452cf008f34faa33776680c26d66f8", size = 6189177 }, +] + +[[package]] +name = "tabulate" +version = "0.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ec/fe/802052aecb21e3797b8f7902564ab6ea0d60ff8ca23952079064155d1ae1/tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c", size = 81090 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/40/44/4a5f08c96eb108af5cb50b41f76142f0afa346dfa99d5296fe7202a11854/tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f", size = 35252 }, +] + +[[package]] +name = "termcolor" +version = "3.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ca/6c/3d75c196ac07ac8749600b60b03f4f6094d54e132c4d94ebac6ee0e0add0/termcolor-3.1.0.tar.gz", hash = "sha256:6a6dd7fbee581909eeec6a756cff1d7f7c376063b14e4a298dc4980309e55970", size = 14324 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4f/bd/de8d508070629b6d84a30d01d57e4a65c69aa7f5abe7560b8fad3b50ea59/termcolor-3.1.0-py3-none-any.whl", hash = "sha256:591dd26b5c2ce03b9e43f391264626557873ce1d379019786f99b0c2bee140aa", size = 7684 }, +] + +[[package]] +name = "threadpoolctl" +version = "3.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b7/4d/08c89e34946fce2aec4fbb45c9016efd5f4d7f24af8e5d93296e935631d8/threadpoolctl-3.6.0.tar.gz", hash = "sha256:8ab8b4aa3491d812b623328249fab5302a68d2d71745c8a4c719a2fcaba9f44e", size = 21274 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/32/d5/f9a850d79b0851d1d4ef6456097579a9005b31fea68726a4ae5f2d82ddd9/threadpoolctl-3.6.0-py3-none-any.whl", hash = "sha256:43a0b8fd5a2928500110039e43a5eed8480b918967083ea48dc3ab9f13c4a7fb", size = 18638 }, +] + +[[package]] +name = "tifffile" +version = "2025.6.11" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/11/9e/636e3e433c24da41dd639e0520db60750dbf5e938d023b83af8097382ea3/tifffile-2025.6.11.tar.gz", hash = "sha256:0ece4c2e7a10656957d568a093b07513c0728d30c1bd8cc12725901fffdb7143", size = 370125 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3a/d8/1ba8f32bfc9cb69e37edeca93738e883f478fbe84ae401f72c0d8d507841/tifffile-2025.6.11-py3-none-any.whl", hash = "sha256:32effb78b10b3a283eb92d4ebf844ae7e93e151458b0412f38518b4e6d2d7542", size = 230800 }, +] + +[[package]] +name = "timm" +version = "0.9.11" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "huggingface-hub" }, + { name = "pyyaml" }, + { name = "safetensors" }, + { name = "torch" }, + { name = "torchvision" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/99/f5/c0306adbd9ffa41b80d0b564b498794356e4986f064351e1ea0d55f2b60f/timm-0.9.11.tar.gz", hash = "sha256:728b433eb4cf0bad0c22a037058388fe7c94662f9b9f7d9f5f7dbe627b41c7fc", size = 2127616 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/aa/4b54f6047c442883243f68f6f9e3a0ab77aaae4b3e6e51a98b371e73dd77/timm-0.9.11-py3-none-any.whl", hash = "sha256:02bba56786633ff46b55ee0ce3b991fa85375556844e500ad18e6b12921dc3da", size = 2231391 }, +] + +[[package]] +name = "tokenizers" +version = "0.21.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "huggingface-hub" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/92/76/5ac0c97f1117b91b7eb7323dcd61af80d72f790b4df71249a7850c195f30/tokenizers-0.21.1.tar.gz", hash = "sha256:a1bb04dc5b448985f86ecd4b05407f5a8d97cb2c0532199b2a302a604a0165ab", size = 343256 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a5/1f/328aee25f9115bf04262e8b4e5a2050b7b7cf44b59c74e982db7270c7f30/tokenizers-0.21.1-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:e78e413e9e668ad790a29456e677d9d3aa50a9ad311a40905d6861ba7692cf41", size = 2780767 }, + { url = "https://files.pythonhosted.org/packages/ae/1a/4526797f3719b0287853f12c5ad563a9be09d446c44ac784cdd7c50f76ab/tokenizers-0.21.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:cd51cd0a91ecc801633829fcd1fda9cf8682ed3477c6243b9a095539de4aecf3", size = 2650555 }, + { url = "https://files.pythonhosted.org/packages/4d/7a/a209b29f971a9fdc1da86f917fe4524564924db50d13f0724feed37b2a4d/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28da6b72d4fb14ee200a1bd386ff74ade8992d7f725f2bde2c495a9a98cf4d9f", size = 2937541 }, + { url = "https://files.pythonhosted.org/packages/3c/1e/b788b50ffc6191e0b1fc2b0d49df8cff16fe415302e5ceb89f619d12c5bc/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:34d8cfde551c9916cb92014e040806122295a6800914bab5865deb85623931cf", size = 2819058 }, + { url = "https://files.pythonhosted.org/packages/36/aa/3626dfa09a0ecc5b57a8c58eeaeb7dd7ca9a37ad9dd681edab5acd55764c/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aaa852d23e125b73d283c98f007e06d4595732104b65402f46e8ef24b588d9f8", size = 3133278 }, + { url = "https://files.pythonhosted.org/packages/a4/4d/8fbc203838b3d26269f944a89459d94c858f5b3f9a9b6ee9728cdcf69161/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a21a15d5c8e603331b8a59548bbe113564136dc0f5ad8306dd5033459a226da0", size = 3144253 }, + { url = "https://files.pythonhosted.org/packages/d8/1b/2bd062adeb7c7511b847b32e356024980c0ffcf35f28947792c2d8ad2288/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2fdbd4c067c60a0ac7eca14b6bd18a5bebace54eb757c706b47ea93204f7a37c", size = 3398225 }, + { url = "https://files.pythonhosted.org/packages/8a/63/38be071b0c8e06840bc6046991636bcb30c27f6bb1e670f4f4bc87cf49cc/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2dd9a0061e403546f7377df940e866c3e678d7d4e9643d0461ea442b4f89e61a", size = 3038874 }, + { url = "https://files.pythonhosted.org/packages/ec/83/afa94193c09246417c23a3c75a8a0a96bf44ab5630a3015538d0c316dd4b/tokenizers-0.21.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:db9484aeb2e200c43b915a1a0150ea885e35f357a5a8fabf7373af333dcc8dbf", size = 9014448 }, + { url = "https://files.pythonhosted.org/packages/ae/b3/0e1a37d4f84c0f014d43701c11eb8072704f6efe8d8fc2dcdb79c47d76de/tokenizers-0.21.1-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:ed248ab5279e601a30a4d67bdb897ecbe955a50f1e7bb62bd99f07dd11c2f5b6", size = 8937877 }, + { url = "https://files.pythonhosted.org/packages/ac/33/ff08f50e6d615eb180a4a328c65907feb6ded0b8f990ec923969759dc379/tokenizers-0.21.1-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:9ac78b12e541d4ce67b4dfd970e44c060a2147b9b2a21f509566d556a509c67d", size = 9186645 }, + { url = "https://files.pythonhosted.org/packages/5f/aa/8ae85f69a9f6012c6f8011c6f4aa1c96154c816e9eea2e1b758601157833/tokenizers-0.21.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:e5a69c1a4496b81a5ee5d2c1f3f7fbdf95e90a0196101b0ee89ed9956b8a168f", size = 9384380 }, + { url = "https://files.pythonhosted.org/packages/e8/5b/a5d98c89f747455e8b7a9504910c865d5e51da55e825a7ae641fb5ff0a58/tokenizers-0.21.1-cp39-abi3-win32.whl", hash = "sha256:1039a3a5734944e09de1d48761ade94e00d0fa760c0e0551151d4dd851ba63e3", size = 2239506 }, + { url = "https://files.pythonhosted.org/packages/e6/b6/072a8e053ae600dcc2ac0da81a23548e3b523301a442a6ca900e92ac35be/tokenizers-0.21.1-cp39-abi3-win_amd64.whl", hash = "sha256:0f0dcbcc9f6e13e675a66d7a5f2f225a736745ce484c1a4e07476a89ccdad382", size = 2435481 }, +] + +[[package]] +name = "torch" +version = "2.6.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "filelock" }, + { name = "fsspec" }, + { name = "jinja2" }, + { name = "networkx" }, + { name = "nvidia-cublas-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cuda-cupti-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cuda-nvrtc-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cuda-runtime-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cudnn-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cufft-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-curand-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cusolver-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cusparse-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cusparselt-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nccl-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nvjitlink-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nvtx-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "setuptools", marker = "python_full_version >= '3.12'" }, + { name = "sympy" }, + { name = "triton", version = "3.2.0", source = { registry = "https://pypi.org/simple" }, marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "typing-extensions" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/a9/97cbbc97002fff0de394a2da2cdfa859481fdca36996d7bd845d50aa9d8d/torch-2.6.0-cp311-cp311-manylinux1_x86_64.whl", hash = "sha256:7979834102cd5b7a43cc64e87f2f3b14bd0e1458f06e9f88ffa386d07c7446e1", size = 766715424 }, + { url = "https://files.pythonhosted.org/packages/6d/fa/134ce8f8a7ea07f09588c9cc2cea0d69249efab977707cf67669431dcf5c/torch-2.6.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:ccbd0320411fe1a3b3fec7b4d3185aa7d0c52adac94480ab024b5c8f74a0bf1d", size = 95759416 }, + { url = "https://files.pythonhosted.org/packages/11/c5/2370d96b31eb1841c3a0883a492c15278a6718ccad61bb6a649c80d1d9eb/torch-2.6.0-cp311-cp311-win_amd64.whl", hash = "sha256:46763dcb051180ce1ed23d1891d9b1598e07d051ce4c9d14307029809c4d64f7", size = 204164970 }, + { url = "https://files.pythonhosted.org/packages/0b/fa/f33a4148c6fb46ca2a3f8de39c24d473822d5774d652b66ed9b1214da5f7/torch-2.6.0-cp311-none-macosx_11_0_arm64.whl", hash = "sha256:94fc63b3b4bedd327af588696559f68c264440e2503cc9e6954019473d74ae21", size = 66530713 }, + { url = "https://files.pythonhosted.org/packages/e5/35/0c52d708144c2deb595cd22819a609f78fdd699b95ff6f0ebcd456e3c7c1/torch-2.6.0-cp312-cp312-manylinux1_x86_64.whl", hash = "sha256:2bb8987f3bb1ef2675897034402373ddfc8f5ef0e156e2d8cfc47cacafdda4a9", size = 766624563 }, + { url = "https://files.pythonhosted.org/packages/01/d6/455ab3fbb2c61c71c8842753b566012e1ed111e7a4c82e0e1c20d0c76b62/torch-2.6.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:b789069020c5588c70d5c2158ac0aa23fd24a028f34a8b4fcb8fcb4d7efcf5fb", size = 95607867 }, + { url = "https://files.pythonhosted.org/packages/18/cf/ae99bd066571656185be0d88ee70abc58467b76f2f7c8bfeb48735a71fe6/torch-2.6.0-cp312-cp312-win_amd64.whl", hash = "sha256:7e1448426d0ba3620408218b50aa6ada88aeae34f7a239ba5431f6c8774b1239", size = 204120469 }, + { url = "https://files.pythonhosted.org/packages/81/b4/605ae4173aa37fb5aa14605d100ff31f4f5d49f617928c9f486bb3aaec08/torch-2.6.0-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:9a610afe216a85a8b9bc9f8365ed561535c93e804c2a317ef7fabcc5deda0989", size = 66532538 }, + { url = "https://files.pythonhosted.org/packages/24/85/ead1349fc30fe5a32cadd947c91bda4a62fbfd7f8c34ee61f6398d38fb48/torch-2.6.0-cp313-cp313-manylinux1_x86_64.whl", hash = "sha256:4874a73507a300a5d089ceaff616a569e7bb7c613c56f37f63ec3ffac65259cf", size = 766626191 }, + { url = "https://files.pythonhosted.org/packages/dd/b0/26f06f9428b250d856f6d512413e9e800b78625f63801cbba13957432036/torch-2.6.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:a0d5e1b9874c1a6c25556840ab8920569a7a4137afa8a63a32cee0bc7d89bd4b", size = 95611439 }, + { url = "https://files.pythonhosted.org/packages/c2/9c/fc5224e9770c83faed3a087112d73147cd7c7bfb7557dcf9ad87e1dda163/torch-2.6.0-cp313-cp313-win_amd64.whl", hash = "sha256:510c73251bee9ba02ae1cb6c9d4ee0907b3ce6020e62784e2d7598e0cfa4d6cc", size = 204126475 }, + { url = "https://files.pythonhosted.org/packages/88/8b/d60c0491ab63634763be1537ad488694d316ddc4a20eaadd639cedc53971/torch-2.6.0-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:ff96f4038f8af9f7ec4231710ed4549da1bdebad95923953a25045dcf6fd87e2", size = 66536783 }, +] + +[[package]] +name = "torchmetrics" +version = "1.7.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "lightning-utilities" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "torch" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b7/ec/f5a4f94c77a1b4c0a37e5c5c8b666a33bc074130258a6b655346bec560c2/torchmetrics-1.7.2.tar.gz", hash = "sha256:ba401cd01aeaa268e809c0e4f42ef8f95669bf9b485e1d93d54dc765e012338a", size = 566185 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/56/89/b5fd7eb99b27457d71d3b7d9eca0b884fa5992abca7672aab1177c5f22d8/torchmetrics-1.7.2-py3-none-any.whl", hash = "sha256:9cc3bff07a715fcb37fb04d2a1a5ae36267c36066c097578020056653a94f2a8", size = 962510 }, +] + +[[package]] +name = "torchvision" +version = "0.21.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, + { name = "pillow" }, + { name = "torch" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/4e/3d/b7241abfa3e6651c6e00796f5de2bd1ce4d500bf5159bcbfeea47e711b93/torchvision-0.21.0-1-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:ff96666b94a55e802ea6796cabe788541719e6f4905fc59c380fed3517b6a64d", size = 2329320 }, + { url = "https://files.pythonhosted.org/packages/52/5b/76ca113a853b19c7b1da761f8a72cb6429b3bd0bf932537d8df4657f47c3/torchvision-0.21.0-1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:ffa2a16499508fe6798323e455f312c7c55f2a88901c9a7c0fb1efa86cf7e327", size = 2329878 }, + { url = "https://files.pythonhosted.org/packages/4e/fe/5e193353706dab96fe73ae100d5a633ff635ce310e0d92f3bc2958d075b1/torchvision-0.21.0-1-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:7e9e9afa150e40cd2a8f0701c43cb82a8d724f512896455c0918b987f94b84a4", size = 2280711 }, + { url = "https://files.pythonhosted.org/packages/29/88/00c69db213ee2443ada8886ec60789b227e06bb869d85ee324578221a7f7/torchvision-0.21.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:110d115333524d60e9e474d53c7d20f096dbd8a080232f88dddb90566f90064c", size = 1784141 }, + { url = "https://files.pythonhosted.org/packages/be/a2/b0cedf0a411f1a5d75cfc0b87cde56dd1ddc1878be46a42c905cd8580220/torchvision-0.21.0-cp311-cp311-manylinux1_x86_64.whl", hash = "sha256:3891cd086c5071bda6b4ee9d266bb2ac39c998c045c2ebcd1e818b8316fb5d41", size = 7237719 }, + { url = "https://files.pythonhosted.org/packages/8c/a1/ee962ef9d0b2bf7a6f8b14cb95acb70e05cd2101af521032a09e43f8582f/torchvision-0.21.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:54454923a50104c66a9ab6bd8b73a11c2fc218c964b1006d5d1fe5b442c3dcb6", size = 14700617 }, + { url = "https://files.pythonhosted.org/packages/88/53/4ad334b9b1d8dd99836869fec139cb74a27781298360b91b9506c53f1d10/torchvision-0.21.0-cp311-cp311-win_amd64.whl", hash = "sha256:49bcfad8cfe2c27dee116c45d4f866d7974bcf14a5a9fbef893635deae322f2f", size = 1560523 }, + { url = "https://files.pythonhosted.org/packages/6e/1b/28f527b22d5e8800184d0bc847f801ae92c7573a8c15979d92b7091c0751/torchvision-0.21.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:97a5814a93c793aaf0179cfc7f916024f4b63218929aee977b645633d074a49f", size = 1784140 }, + { url = "https://files.pythonhosted.org/packages/36/63/0722e153fd27d64d5b0af45b5c8cb0e80b35a68cf0130303bc9a8bb095c7/torchvision-0.21.0-cp312-cp312-manylinux1_x86_64.whl", hash = "sha256:b578bcad8a4083b40d34f689b19ca9f7c63e511758d806510ea03c29ac568f7b", size = 7238673 }, + { url = "https://files.pythonhosted.org/packages/bb/ea/03541ed901cdc30b934f897060d09bbf7a98466a08ad1680320f9ce0cbe0/torchvision-0.21.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:5083a5b1fec2351bf5ea9900a741d54086db75baec4b1d21e39451e00977f1b1", size = 14701186 }, + { url = "https://files.pythonhosted.org/packages/4c/6a/c7752603060d076dfed95135b78b047dc71792630cbcb022e3693d6f32ef/torchvision-0.21.0-cp312-cp312-win_amd64.whl", hash = "sha256:6eb75d41e3bbfc2f7642d0abba9383cc9ae6c5a4ca8d6b00628c225e1eaa63b3", size = 1560520 }, + { url = "https://files.pythonhosted.org/packages/f9/56/47d456b61c3bbce7bed4af3925c83d405bb87468e659fd3cf3d9840c3b51/torchvision-0.21.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:659b76c86757cb2ee4ca2db245e0740cfc3081fef46f0f1064d11adb4a8cee31", size = 1784141 }, + { url = "https://files.pythonhosted.org/packages/cb/4c/99880813aa50e64447fb1c4c6c804a793d2d78f7f7c53e99ddee7fa175fa/torchvision-0.21.0-cp313-cp313-manylinux1_x86_64.whl", hash = "sha256:084ac3f5a1f50c70d630a488d19bf62f323018eae1b1c1232f2b7047d3a7b76d", size = 7238714 }, + { url = "https://files.pythonhosted.org/packages/0b/2d/3c3ee10608310a395594aac7da8640372ed79c6585910ccae6919658dcdc/torchvision-0.21.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:5045a3a5f21ec3eea6962fa5f2fa2d4283f854caec25ada493fcf4aab2925467", size = 2281252 }, + { url = "https://files.pythonhosted.org/packages/ed/b4/fc60e3bc003879d3de842baea258fffc3586f4b49cd435a5ba1e09c33315/torchvision-0.21.0-cp313-cp313-win_amd64.whl", hash = "sha256:9147f5e096a9270684e3befdee350f3cacafd48e0c54ab195f45790a9c146d67", size = 1560519 }, +] + +[[package]] +name = "tornado" +version = "6.5.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/51/89/c72771c81d25d53fe33e3dca61c233b665b2780f21820ba6fd2c6793c12b/tornado-6.5.1.tar.gz", hash = "sha256:84ceece391e8eb9b2b95578db65e920d2a61070260594819589609ba9bc6308c", size = 509934 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/77/89/f4532dee6843c9e0ebc4e28d4be04c67f54f60813e4bf73d595fe7567452/tornado-6.5.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:d50065ba7fd11d3bd41bcad0825227cc9a95154bad83239357094c36708001f7", size = 441948 }, + { url = "https://files.pythonhosted.org/packages/15/9a/557406b62cffa395d18772e0cdcf03bed2fff03b374677348eef9f6a3792/tornado-6.5.1-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:9e9ca370f717997cb85606d074b0e5b247282cf5e2e1611568b8821afe0342d6", size = 440112 }, + { url = "https://files.pythonhosted.org/packages/55/82/7721b7319013a3cf881f4dffa4f60ceff07b31b394e459984e7a36dc99ec/tornado-6.5.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b77e9dfa7ed69754a54c89d82ef746398be82f749df69c4d3abe75c4d1ff4888", size = 443672 }, + { url = "https://files.pythonhosted.org/packages/7d/42/d11c4376e7d101171b94e03cef0cbce43e823ed6567ceda571f54cf6e3ce/tornado-6.5.1-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:253b76040ee3bab8bcf7ba9feb136436a3787208717a1fb9f2c16b744fba7331", size = 443019 }, + { url = "https://files.pythonhosted.org/packages/7d/f7/0c48ba992d875521ac761e6e04b0a1750f8150ae42ea26df1852d6a98942/tornado-6.5.1-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:308473f4cc5a76227157cdf904de33ac268af770b2c5f05ca6c1161d82fdd95e", size = 443252 }, + { url = "https://files.pythonhosted.org/packages/89/46/d8d7413d11987e316df4ad42e16023cd62666a3c0dfa1518ffa30b8df06c/tornado-6.5.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:caec6314ce8a81cf69bd89909f4b633b9f523834dc1a352021775d45e51d9401", size = 443930 }, + { url = "https://files.pythonhosted.org/packages/78/b2/f8049221c96a06df89bed68260e8ca94beca5ea532ffc63b1175ad31f9cc/tornado-6.5.1-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:13ce6e3396c24e2808774741331638ee6c2f50b114b97a55c5b442df65fd9692", size = 443351 }, + { url = "https://files.pythonhosted.org/packages/76/ff/6a0079e65b326cc222a54720a748e04a4db246870c4da54ece4577bfa702/tornado-6.5.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:5cae6145f4cdf5ab24744526cc0f55a17d76f02c98f4cff9daa08ae9a217448a", size = 443328 }, + { url = "https://files.pythonhosted.org/packages/49/18/e3f902a1d21f14035b5bc6246a8c0f51e0eef562ace3a2cea403c1fb7021/tornado-6.5.1-cp39-abi3-win32.whl", hash = "sha256:e0a36e1bc684dca10b1aa75a31df8bdfed656831489bc1e6a6ebed05dc1ec365", size = 444396 }, + { url = "https://files.pythonhosted.org/packages/7b/09/6526e32bf1049ee7de3bebba81572673b19a2a8541f795d887e92af1a8bc/tornado-6.5.1-cp39-abi3-win_amd64.whl", hash = "sha256:908e7d64567cecd4c2b458075589a775063453aeb1d2a1853eedb806922f568b", size = 444840 }, + { url = "https://files.pythonhosted.org/packages/55/a7/535c44c7bea4578e48281d83c615219f3ab19e6abc67625ef637c73987be/tornado-6.5.1-cp39-abi3-win_arm64.whl", hash = "sha256:02420a0eb7bf617257b9935e2b754d1b63897525d8a289c9d65690d580b4dcf7", size = 443596 }, +] + +[[package]] +name = "tqdm" +version = "4.67.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2", size = 169737 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540 }, +] + +[[package]] +name = "traitlets" +version = "5.14.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/eb/79/72064e6a701c2183016abbbfedaba506d81e30e232a68c9f0d6f6fcd1574/traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7", size = 161621 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f", size = 85359 }, +] + +[[package]] +name = "transformers" +version = "4.52.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "filelock" }, + { name = "huggingface-hub" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "pyyaml" }, + { name = "regex" }, + { name = "requests" }, + { name = "safetensors" }, + { name = "tokenizers" }, + { name = "tqdm" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/da/a9/275037087f9d846580b02f2d7cae0e0a6955d46f84583d0151d6227bd416/transformers-4.52.4.tar.gz", hash = "sha256:aff3764441c1adc192a08dba49740d3cbbcb72d850586075aed6bd89b98203e6", size = 8945376 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/96/f2/25b27b396af03d5b64e61976b14f7209e2939e9e806c10749b6d277c273e/transformers-4.52.4-py3-none-any.whl", hash = "sha256:203f5c19416d5877e36e88633943761719538a25d9775977a24fe77a1e5adfc7", size = 10460375 }, +] + +[[package]] +name = "triton" +version = "3.2.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.12' and platform_machine != 'aarch64' and sys_platform == 'linux'", + "python_full_version == '3.12.*' and platform_machine != 'aarch64' and sys_platform == 'linux'", + "python_full_version >= '3.13' and platform_machine != 'aarch64' and sys_platform == 'linux'", +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/a7/2e/757d2280d4fefe7d33af7615124e7e298ae7b8e3bc4446cdb8e88b0f9bab/triton-3.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8009a1fb093ee8546495e96731336a33fb8856a38e45bb4ab6affd6dbc3ba220", size = 253157636 }, + { url = "https://files.pythonhosted.org/packages/06/00/59500052cb1cf8cf5316be93598946bc451f14072c6ff256904428eaf03c/triton-3.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d9b215efc1c26fa7eefb9a157915c92d52e000d2bf83e5f69704047e63f125c", size = 253159365 }, + { url = "https://files.pythonhosted.org/packages/c7/30/37a3384d1e2e9320331baca41e835e90a3767303642c7a80d4510152cbcf/triton-3.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e5dfa23ba84541d7c0a531dfce76d8bcd19159d50a4a8b14ad01e91734a5c1b0", size = 253154278 }, +] + +[[package]] +name = "triton" +version = "3.3.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.12' and sys_platform == 'darwin'", + "python_full_version < '3.12' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "python_full_version < '3.12' and sys_platform != 'darwin' and sys_platform != 'linux'", + "python_full_version == '3.12.*' and sys_platform == 'darwin'", + "python_full_version >= '3.13' and sys_platform == 'darwin'", + "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux'", + "python_full_version >= '3.13' and sys_platform != 'darwin' and sys_platform != 'linux'", +] +dependencies = [ + { name = "setuptools", marker = "platform_machine == 'aarch64' or sys_platform != 'linux'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/3c/c5/4874a81131cc9e934d88377fbc9d24319ae1fb540f3333b4e9c696ebc607/triton-3.3.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3161a2bf073d6b22c4e2f33f951f3e5e3001462b2570e6df9cd57565bdec2984", size = 156528461 }, + { url = "https://files.pythonhosted.org/packages/11/53/ce18470914ab6cfbec9384ee565d23c4d1c55f0548160b1c7b33000b11fd/triton-3.3.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b68c778f6c4218403a6bd01be7484f6dc9e20fe2083d22dd8aef33e3b87a10a3", size = 156504509 }, + { url = "https://files.pythonhosted.org/packages/7d/74/4bf2702b65e93accaa20397b74da46fb7a0356452c1bb94dbabaf0582930/triton-3.3.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:47bc87ad66fa4ef17968299acacecaab71ce40a238890acc6ad197c3abe2b8f1", size = 156516468 }, + { url = "https://files.pythonhosted.org/packages/0a/93/f28a696fa750b9b608baa236f8225dd3290e5aff27433b06143adc025961/triton-3.3.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ce4700fc14032af1e049005ae94ba908e71cd6c2df682239aed08e49bc71b742", size = 156580729 }, +] + +[[package]] +name = "typing-extensions" +version = "4.14.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d1/bc/51647cd02527e87d05cb083ccc402f93e441606ff1f01739a62c8ad09ba5/typing_extensions-4.14.0.tar.gz", hash = "sha256:8676b788e32f02ab42d9e7c61324048ae4c6d844a399eebace3d4979d75ceef4", size = 107423 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/69/e0/552843e0d356fbb5256d21449fa957fa4eff3bbc135a74a691ee70c7c5da/typing_extensions-4.14.0-py3-none-any.whl", hash = "sha256:a1514509136dd0b477638fc68d6a91497af5076466ad0fa6c338e44e359944af", size = 43839 }, +] + +[[package]] +name = "typing-inspection" +version = "0.4.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f8/b1/0c11f5058406b3af7609f121aaa6b609744687f1d158b3c3a5bf4cc94238/typing_inspection-0.4.1.tar.gz", hash = "sha256:6ae134cc0203c33377d43188d4064e9b357dba58cff3185f22924610e70a9d28", size = 75726 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/17/69/cd203477f944c353c31bade965f880aa1061fd6bf05ded0726ca845b6ff7/typing_inspection-0.4.1-py3-none-any.whl", hash = "sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51", size = 14552 }, +] + +[[package]] +name = "tzdata" +version = "2025.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/95/32/1a225d6164441be760d75c2c42e2780dc0873fe382da3e98a2e1e48361e5/tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9", size = 196380 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5c/23/c7abc0ca0a1526a0774eca151daeb8de62ec457e77262b66b359c3c7679e/tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8", size = 347839 }, +] + +[[package]] +name = "uni" +version = "0.1.0" +source = { git = "https://github.com/mahmoodlab/UNI.git#42715efc11722a496e0a67f3369505a8f277206c" } +dependencies = [ + { name = "numpy" }, + { name = "pandas" }, + { name = "scikit-learn" }, + { name = "timm" }, + { name = "torch" }, + { name = "torchvision" }, + { name = "tqdm" }, + { name = "transformers" }, + { name = "xformers", marker = "sys_platform != 'darwin'" }, +] + +[[package]] +name = "urllib3" +version = "2.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8a/78/16493d9c386d8e60e442a35feac5e00f0913c0f4b7c217c11e8ec2ff53e0/urllib3-2.4.0.tar.gz", hash = "sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466", size = 390672 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6b/11/cc635220681e93a0183390e26485430ca2c7b5f9d33b15c74c2861cb8091/urllib3-2.4.0-py3-none-any.whl", hash = "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813", size = 128680 }, +] + +[[package]] +name = "wadler-lindig" +version = "0.1.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4d/c8/e2112ecb627e01c9e2911f9b388167231c23a114946946d046f4e9535118/wadler_lindig-0.1.6.tar.gz", hash = "sha256:8b6adad9718291a7d82fb088a596b93659ce2346321ca76819810affbc66102b", size = 15812 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/9a/937038f3efc70871fb26b0ee6148efcfcfb96643c517c2aaddd7ed07ad76/wadler_lindig-0.1.6-py3-none-any.whl", hash = "sha256:d707f63994c7d3e1e125e7fb7e196f4adb6f80f4a11beb955c6da937754026a3", size = 20483 }, +] + +[[package]] +name = "wandb" +version = "0.19.11" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "docker-pycreds" }, + { name = "gitpython" }, + { name = "platformdirs" }, + { name = "protobuf" }, + { name = "psutil" }, + { name = "pydantic" }, + { name = "pyyaml" }, + { name = "requests" }, + { name = "sentry-sdk" }, + { name = "setproctitle" }, + { name = "setuptools" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/39/98/0ff2925a21b998d4b84731429f4554ca3d9b5cad42c09c075e7306c3aca0/wandb-0.19.11.tar.gz", hash = "sha256:3f50a27dfadbb25946a513ffe856c0e8e538b5626ef207aa50b00c3b0356bff8", size = 39511477 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4f/2c/f8bab58c73fdde4442f1baffd9ea5d1bb3113906a97a27e8d9ab72db7a69/wandb-0.19.11-py3-none-any.whl", hash = "sha256:ff3bf050ba25ebae7aedc9a775ffab90c28068832edfe5458423f488c2558f82", size = 6481327 }, + { url = "https://files.pythonhosted.org/packages/45/4a/34b364280f690f4c6d7660f528fba9f13bdecabc4c869d266a4632cf836e/wandb-0.19.11-py3-none-macosx_10_14_x86_64.whl", hash = "sha256:0823fd9aa6343f40c04e01959997ca8c6d6adf1bd81c8d45261fa4915f1c6b67", size = 20555751 }, + { url = "https://files.pythonhosted.org/packages/d8/e6/a27868fdb83a60df37b9d15e52c3353dd88d74442f27ae48cf765c6b9554/wandb-0.19.11-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c758ef5439599d9023db5b3cf1698477055d82f9fae48af2779f63f1d289167c", size = 20377587 }, + { url = "https://files.pythonhosted.org/packages/21/f7/d5cf5b58c2b3015364c7b2b6af6a440cbeda4103b67332e1e64b30f6252d/wandb-0.19.11-py3-none-macosx_11_0_x86_64.whl", hash = "sha256:de2dfd4911e7691735e271654c735e7b90cdee9d29a3796fbf06e9e92d48f3d7", size = 20985041 }, + { url = "https://files.pythonhosted.org/packages/68/06/8b827f16a0b8f18002d2fffa7c5a7fd447946e0d0c68aeec0dd7eb18cdd3/wandb-0.19.11-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cfff738850770d26b13f8f3fe400a6456f1e39e87f3f29d5aa241b249476df95", size = 20017696 }, + { url = "https://files.pythonhosted.org/packages/f9/31/eeb2878b26566c04c3e9b8b20b3ec3c54a2be50535088d36a37c008e07a3/wandb-0.19.11-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e8ff673007448df11cc69379ae0df28ead866800dc1ec7bc151b402db0bbcf40", size = 21425857 }, + { url = "https://files.pythonhosted.org/packages/10/30/08988360678ae78334bb16625c28260fcaba49f500b89f8766807cb74d71/wandb-0.19.11-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:858bc5023fa1b3285d89d15f62be78afdb28301064daa49ea3f4ebde5dcedad2", size = 20023145 }, + { url = "https://files.pythonhosted.org/packages/c8/e9/a639c42c8ca517c4d25e8970d64d0c5a9bd35b784faed5f47d9cca3dcd12/wandb-0.19.11-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:90e4b57649896acb16c3dd41b3093df1a169c2f1d94ff15d76af86b8a60dcdac", size = 21504842 }, + { url = "https://files.pythonhosted.org/packages/44/74/dbe9277dd935b77dd16939cdf15357766fec0813a6e336cf5f1d07eb016e/wandb-0.19.11-py3-none-win32.whl", hash = "sha256:38dea43c7926d8800405a73b80b9adfe81eb315fc6f2ac6885c77eb966634421", size = 20767584 }, + { url = "https://files.pythonhosted.org/packages/36/d5/215cac3edec5c5ac6e7231beb9d22466d5d4e4a132fa3a1d044f7d682c15/wandb-0.19.11-py3-none-win_amd64.whl", hash = "sha256:73402003c56ddc2198878492ab2bff55bb49bce5587eae5960e737d27c0c48f7", size = 20767588 }, +] + +[[package]] +name = "wcwidth" +version = "0.2.13" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6c/63/53559446a878410fc5a5974feb13d31d78d752eb18aeba59c7fef1af7598/wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5", size = 101301 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fd/84/fd2ba7aafacbad3c4201d395674fc6348826569da3c0937e75505ead3528/wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859", size = 34166 }, +] + +[[package]] +name = "webdataset" +version = "1.0.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "braceexpand" }, + { name = "numpy" }, + { name = "pyyaml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5a/3a/68800d92e065cf4750ebecf973b13979c0c929b439e1293012938862038d/webdataset-1.0.2.tar.gz", hash = "sha256:7f0498be827cfa46cc5430a58768a24e2c6a410676a61be1838f53d61afdaab4", size = 80090 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d9/00/aca6beb3658dab4ed3dbb41a78e6e7f31342e0b41d28088f205525751601/webdataset-1.0.2-py3-none-any.whl", hash = "sha256:3dbfced32b25c0d199c6b9787937b6f85742bc3c84f652c846893075c1c082d9", size = 74956 }, +] + +[[package]] +name = "wrapt" +version = "1.17.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/fc/e91cc220803d7bc4db93fb02facd8461c37364151b8494762cc88b0fbcef/wrapt-1.17.2.tar.gz", hash = "sha256:41388e9d4d1522446fe79d3213196bd9e3b301a336965b9e27ca2788ebd122f3", size = 55531 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cd/f7/a2aab2cbc7a665efab072344a8949a71081eed1d2f451f7f7d2b966594a2/wrapt-1.17.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ff04ef6eec3eee8a5efef2401495967a916feaa353643defcc03fc74fe213b58", size = 53308 }, + { url = "https://files.pythonhosted.org/packages/50/ff/149aba8365fdacef52b31a258c4dc1c57c79759c335eff0b3316a2664a64/wrapt-1.17.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4db983e7bca53819efdbd64590ee96c9213894272c776966ca6306b73e4affda", size = 38488 }, + { url = "https://files.pythonhosted.org/packages/65/46/5a917ce85b5c3b490d35c02bf71aedaa9f2f63f2d15d9949cc4ba56e8ba9/wrapt-1.17.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9abc77a4ce4c6f2a3168ff34b1da9b0f311a8f1cfd694ec96b0603dff1c79438", size = 38776 }, + { url = "https://files.pythonhosted.org/packages/ca/74/336c918d2915a4943501c77566db41d1bd6e9f4dbc317f356b9a244dfe83/wrapt-1.17.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b929ac182f5ace000d459c59c2c9c33047e20e935f8e39371fa6e3b85d56f4a", size = 83776 }, + { url = "https://files.pythonhosted.org/packages/09/99/c0c844a5ccde0fe5761d4305485297f91d67cf2a1a824c5f282e661ec7ff/wrapt-1.17.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f09b286faeff3c750a879d336fb6d8713206fc97af3adc14def0cdd349df6000", size = 75420 }, + { url = "https://files.pythonhosted.org/packages/b4/b0/9fc566b0fe08b282c850063591a756057c3247b2362b9286429ec5bf1721/wrapt-1.17.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a7ed2d9d039bd41e889f6fb9364554052ca21ce823580f6a07c4ec245c1f5d6", size = 83199 }, + { url = "https://files.pythonhosted.org/packages/9d/4b/71996e62d543b0a0bd95dda485219856def3347e3e9380cc0d6cf10cfb2f/wrapt-1.17.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:129a150f5c445165ff941fc02ee27df65940fcb8a22a61828b1853c98763a64b", size = 82307 }, + { url = "https://files.pythonhosted.org/packages/39/35/0282c0d8789c0dc9bcc738911776c762a701f95cfe113fb8f0b40e45c2b9/wrapt-1.17.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1fb5699e4464afe5c7e65fa51d4f99e0b2eadcc176e4aa33600a3df7801d6662", size = 75025 }, + { url = "https://files.pythonhosted.org/packages/4f/6d/90c9fd2c3c6fee181feecb620d95105370198b6b98a0770cba090441a828/wrapt-1.17.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9a2bce789a5ea90e51a02dfcc39e31b7f1e662bc3317979aa7e5538e3a034f72", size = 81879 }, + { url = "https://files.pythonhosted.org/packages/8f/fa/9fb6e594f2ce03ef03eddbdb5f4f90acb1452221a5351116c7c4708ac865/wrapt-1.17.2-cp311-cp311-win32.whl", hash = "sha256:4afd5814270fdf6380616b321fd31435a462019d834f83c8611a0ce7484c7317", size = 36419 }, + { url = "https://files.pythonhosted.org/packages/47/f8/fb1773491a253cbc123c5d5dc15c86041f746ed30416535f2a8df1f4a392/wrapt-1.17.2-cp311-cp311-win_amd64.whl", hash = "sha256:acc130bc0375999da18e3d19e5a86403667ac0c4042a094fefb7eec8ebac7cf3", size = 38773 }, + { url = "https://files.pythonhosted.org/packages/a1/bd/ab55f849fd1f9a58ed7ea47f5559ff09741b25f00c191231f9f059c83949/wrapt-1.17.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d5e2439eecc762cd85e7bd37161d4714aa03a33c5ba884e26c81559817ca0925", size = 53799 }, + { url = "https://files.pythonhosted.org/packages/53/18/75ddc64c3f63988f5a1d7e10fb204ffe5762bc663f8023f18ecaf31a332e/wrapt-1.17.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3fc7cb4c1c744f8c05cd5f9438a3caa6ab94ce8344e952d7c45a8ed59dd88392", size = 38821 }, + { url = "https://files.pythonhosted.org/packages/48/2a/97928387d6ed1c1ebbfd4efc4133a0633546bec8481a2dd5ec961313a1c7/wrapt-1.17.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8fdbdb757d5390f7c675e558fd3186d590973244fab0c5fe63d373ade3e99d40", size = 38919 }, + { url = "https://files.pythonhosted.org/packages/73/54/3bfe5a1febbbccb7a2f77de47b989c0b85ed3a6a41614b104204a788c20e/wrapt-1.17.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5bb1d0dbf99411f3d871deb6faa9aabb9d4e744d67dcaaa05399af89d847a91d", size = 88721 }, + { url = "https://files.pythonhosted.org/packages/25/cb/7262bc1b0300b4b64af50c2720ef958c2c1917525238d661c3e9a2b71b7b/wrapt-1.17.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d18a4865f46b8579d44e4fe1e2bcbc6472ad83d98e22a26c963d46e4c125ef0b", size = 80899 }, + { url = "https://files.pythonhosted.org/packages/2a/5a/04cde32b07a7431d4ed0553a76fdb7a61270e78c5fd5a603e190ac389f14/wrapt-1.17.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc570b5f14a79734437cb7b0500376b6b791153314986074486e0b0fa8d71d98", size = 89222 }, + { url = "https://files.pythonhosted.org/packages/09/28/2e45a4f4771fcfb109e244d5dbe54259e970362a311b67a965555ba65026/wrapt-1.17.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6d9187b01bebc3875bac9b087948a2bccefe464a7d8f627cf6e48b1bbae30f82", size = 86707 }, + { url = "https://files.pythonhosted.org/packages/c6/d2/dcb56bf5f32fcd4bd9aacc77b50a539abdd5b6536872413fd3f428b21bed/wrapt-1.17.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9e8659775f1adf02eb1e6f109751268e493c73716ca5761f8acb695e52a756ae", size = 79685 }, + { url = "https://files.pythonhosted.org/packages/80/4e/eb8b353e36711347893f502ce91c770b0b0929f8f0bed2670a6856e667a9/wrapt-1.17.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e8b2816ebef96d83657b56306152a93909a83f23994f4b30ad4573b00bd11bb9", size = 87567 }, + { url = "https://files.pythonhosted.org/packages/17/27/4fe749a54e7fae6e7146f1c7d914d28ef599dacd4416566c055564080fe2/wrapt-1.17.2-cp312-cp312-win32.whl", hash = "sha256:468090021f391fe0056ad3e807e3d9034e0fd01adcd3bdfba977b6fdf4213ea9", size = 36672 }, + { url = "https://files.pythonhosted.org/packages/15/06/1dbf478ea45c03e78a6a8c4be4fdc3c3bddea5c8de8a93bc971415e47f0f/wrapt-1.17.2-cp312-cp312-win_amd64.whl", hash = "sha256:ec89ed91f2fa8e3f52ae53cd3cf640d6feff92ba90d62236a81e4e563ac0e991", size = 38865 }, + { url = "https://files.pythonhosted.org/packages/ce/b9/0ffd557a92f3b11d4c5d5e0c5e4ad057bd9eb8586615cdaf901409920b14/wrapt-1.17.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6ed6ffac43aecfe6d86ec5b74b06a5be33d5bb9243d055141e8cabb12aa08125", size = 53800 }, + { url = "https://files.pythonhosted.org/packages/c0/ef/8be90a0b7e73c32e550c73cfb2fa09db62234227ece47b0e80a05073b375/wrapt-1.17.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:35621ae4c00e056adb0009f8e86e28eb4a41a4bfa8f9bfa9fca7d343fe94f998", size = 38824 }, + { url = "https://files.pythonhosted.org/packages/36/89/0aae34c10fe524cce30fe5fc433210376bce94cf74d05b0d68344c8ba46e/wrapt-1.17.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a604bf7a053f8362d27eb9fefd2097f82600b856d5abe996d623babd067b1ab5", size = 38920 }, + { url = "https://files.pythonhosted.org/packages/3b/24/11c4510de906d77e0cfb5197f1b1445d4fec42c9a39ea853d482698ac681/wrapt-1.17.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cbabee4f083b6b4cd282f5b817a867cf0b1028c54d445b7ec7cfe6505057cf8", size = 88690 }, + { url = "https://files.pythonhosted.org/packages/71/d7/cfcf842291267bf455b3e266c0c29dcb675b5540ee8b50ba1699abf3af45/wrapt-1.17.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49703ce2ddc220df165bd2962f8e03b84c89fee2d65e1c24a7defff6f988f4d6", size = 80861 }, + { url = "https://files.pythonhosted.org/packages/d5/66/5d973e9f3e7370fd686fb47a9af3319418ed925c27d72ce16b791231576d/wrapt-1.17.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8112e52c5822fc4253f3901b676c55ddf288614dc7011634e2719718eaa187dc", size = 89174 }, + { url = "https://files.pythonhosted.org/packages/a7/d3/8e17bb70f6ae25dabc1aaf990f86824e4fd98ee9cadf197054e068500d27/wrapt-1.17.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9fee687dce376205d9a494e9c121e27183b2a3df18037f89d69bd7b35bcf59e2", size = 86721 }, + { url = "https://files.pythonhosted.org/packages/6f/54/f170dfb278fe1c30d0ff864513cff526d624ab8de3254b20abb9cffedc24/wrapt-1.17.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:18983c537e04d11cf027fbb60a1e8dfd5190e2b60cc27bc0808e653e7b218d1b", size = 79763 }, + { url = "https://files.pythonhosted.org/packages/4a/98/de07243751f1c4a9b15c76019250210dd3486ce098c3d80d5f729cba029c/wrapt-1.17.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:703919b1633412ab54bcf920ab388735832fdcb9f9a00ae49387f0fe67dad504", size = 87585 }, + { url = "https://files.pythonhosted.org/packages/f9/f0/13925f4bd6548013038cdeb11ee2cbd4e37c30f8bfd5db9e5a2a370d6e20/wrapt-1.17.2-cp313-cp313-win32.whl", hash = "sha256:abbb9e76177c35d4e8568e58650aa6926040d6a9f6f03435b7a522bf1c487f9a", size = 36676 }, + { url = "https://files.pythonhosted.org/packages/bf/ae/743f16ef8c2e3628df3ddfd652b7d4c555d12c84b53f3d8218498f4ade9b/wrapt-1.17.2-cp313-cp313-win_amd64.whl", hash = "sha256:69606d7bb691b50a4240ce6b22ebb319c1cfb164e5f6569835058196e0f3a845", size = 38871 }, + { url = "https://files.pythonhosted.org/packages/3d/bc/30f903f891a82d402ffb5fda27ec1d621cc97cb74c16fea0b6141f1d4e87/wrapt-1.17.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:4a721d3c943dae44f8e243b380cb645a709ba5bd35d3ad27bc2ed947e9c68192", size = 56312 }, + { url = "https://files.pythonhosted.org/packages/8a/04/c97273eb491b5f1c918857cd26f314b74fc9b29224521f5b83f872253725/wrapt-1.17.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:766d8bbefcb9e00c3ac3b000d9acc51f1b399513f44d77dfe0eb026ad7c9a19b", size = 40062 }, + { url = "https://files.pythonhosted.org/packages/4e/ca/3b7afa1eae3a9e7fefe499db9b96813f41828b9fdb016ee836c4c379dadb/wrapt-1.17.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e496a8ce2c256da1eb98bd15803a79bee00fc351f5dfb9ea82594a3f058309e0", size = 40155 }, + { url = "https://files.pythonhosted.org/packages/89/be/7c1baed43290775cb9030c774bc53c860db140397047cc49aedaf0a15477/wrapt-1.17.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d615e4fe22f4ad3528448c193b218e077656ca9ccb22ce2cb20db730f8d306", size = 113471 }, + { url = "https://files.pythonhosted.org/packages/32/98/4ed894cf012b6d6aae5f5cc974006bdeb92f0241775addad3f8cd6ab71c8/wrapt-1.17.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a5aaeff38654462bc4b09023918b7f21790efb807f54c000a39d41d69cf552cb", size = 101208 }, + { url = "https://files.pythonhosted.org/packages/ea/fd/0c30f2301ca94e655e5e057012e83284ce8c545df7661a78d8bfca2fac7a/wrapt-1.17.2-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a7d15bbd2bc99e92e39f49a04653062ee6085c0e18b3b7512a4f2fe91f2d681", size = 109339 }, + { url = "https://files.pythonhosted.org/packages/75/56/05d000de894c4cfcb84bcd6b1df6214297b8089a7bd324c21a4765e49b14/wrapt-1.17.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:e3890b508a23299083e065f435a492b5435eba6e304a7114d2f919d400888cc6", size = 110232 }, + { url = "https://files.pythonhosted.org/packages/53/f8/c3f6b2cf9b9277fb0813418e1503e68414cd036b3b099c823379c9575e6d/wrapt-1.17.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:8c8b293cd65ad716d13d8dd3624e42e5a19cc2a2f1acc74b30c2c13f15cb61a6", size = 100476 }, + { url = "https://files.pythonhosted.org/packages/a7/b1/0bb11e29aa5139d90b770ebbfa167267b1fc548d2302c30c8f7572851738/wrapt-1.17.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c82b8785d98cdd9fed4cac84d765d234ed3251bd6afe34cb7ac523cb93e8b4f", size = 106377 }, + { url = "https://files.pythonhosted.org/packages/6a/e1/0122853035b40b3f333bbb25f1939fc1045e21dd518f7f0922b60c156f7c/wrapt-1.17.2-cp313-cp313t-win32.whl", hash = "sha256:13e6afb7fe71fe7485a4550a8844cc9ffbe263c0f1a1eea569bc7091d4898555", size = 37986 }, + { url = "https://files.pythonhosted.org/packages/09/5e/1655cf481e079c1f22d0cabdd4e51733679932718dc23bf2db175f329b76/wrapt-1.17.2-cp313-cp313t-win_amd64.whl", hash = "sha256:eaf675418ed6b3b31c7a989fd007fa7c3be66ce14e5c3b27336383604c9da85c", size = 40750 }, + { url = "https://files.pythonhosted.org/packages/2d/82/f56956041adef78f849db6b289b282e72b55ab8045a75abad81898c28d19/wrapt-1.17.2-py3-none-any.whl", hash = "sha256:b18f2d1533a71f069c7f82d524a52599053d4c7166e9dd374ae2136b7f40f7c8", size = 23594 }, +] + +[[package]] +name = "xformers" +version = "0.0.29.post3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", marker = "sys_platform != 'darwin'" }, + { name = "torch", marker = "sys_platform != 'darwin'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c1/fd/e9201fbee6a1a6d7a9c67c24a256ad4c2377bc67a634f7dbeaea23bd668a/xformers-0.0.29.post3.tar.gz", hash = "sha256:0b77c67ecc3c9fdd8a0e4399e675adf12e2ff40285e00974cca2d09108157f60", size = 8461348 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/55/05/9c9faf1c7b3b7b986bbf7a488a185eb67670a8435d0eae94aa59f56181cd/xformers-0.0.29.post3-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:bbf2f500dfdbcf4649bf568cc2c9f434399f704dc4064fd1fbdbef2b524a8139", size = 43362399 }, + { url = "https://files.pythonhosted.org/packages/e0/9f/8195d17a5ad1b601bb487f24e54331d102df7f1649e2ced6375eef272e28/xformers-0.0.29.post3-cp311-cp311-win_amd64.whl", hash = "sha256:00f2dfd94c894ff6372e21bee3f09e96bce75b55649df366649c43f049eb7a1e", size = 167742633 }, + { url = "https://files.pythonhosted.org/packages/2d/4a/20b2d9ac50efa0d40fbdb13283fd168cc2db28a2f21a159abbdd17a24213/xformers-0.0.29.post3-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:08fa92f3e06372c4ce2a5306c54ae3d4a3a399fc7e24e02aac3761112ec3aeed", size = 43364118 }, + { url = "https://files.pythonhosted.org/packages/d9/ec/7846937d26b2601e40cd6e64583657f753415b94ad318e4ca350270e77d2/xformers-0.0.29.post3-cp312-cp312-win_amd64.whl", hash = "sha256:3706eca371767ff9709595185910d809fc817ec3cf4234ef44d70d2b8844d7e2", size = 167743565 }, +] + +[[package]] +name = "yacs" +version = "0.1.8" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyyaml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/44/3e/4a45cb0738da6565f134c01d82ba291c746551b5bc82e781ec876eb20909/yacs-0.1.8.tar.gz", hash = "sha256:efc4c732942b3103bea904ee89af98bcd27d01f0ac12d8d4d369f1e7a2914384", size = 11100 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/38/4f/fe9a4d472aa867878ce3bb7efb16654c5d63672b86dc0e6e953a67018433/yacs-0.1.8-py3-none-any.whl", hash = "sha256:99f893e30497a4b66842821bac316386f7bd5c4f47ad35c9073ef089aa33af32", size = 14747 }, +] + +[[package]] +name = "yarl" +version = "1.20.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, + { name = "multidict" }, + { name = "propcache" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/62/51/c0edba5219027f6eab262e139f73e2417b0f4efffa23bf562f6e18f76ca5/yarl-1.20.0.tar.gz", hash = "sha256:686d51e51ee5dfe62dec86e4866ee0e9ed66df700d55c828a615640adc885307", size = 185258 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/60/82/a59d8e21b20ffc836775fa7daedac51d16bb8f3010c4fcb495c4496aa922/yarl-1.20.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:fdb5204d17cb32b2de2d1e21c7461cabfacf17f3645e4b9039f210c5d3378bf3", size = 145178 }, + { url = "https://files.pythonhosted.org/packages/ba/81/315a3f6f95947cfbf37c92d6fbce42a1a6207b6c38e8c2b452499ec7d449/yarl-1.20.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:eaddd7804d8e77d67c28d154ae5fab203163bd0998769569861258e525039d2a", size = 96859 }, + { url = "https://files.pythonhosted.org/packages/ad/17/9b64e575583158551b72272a1023cdbd65af54fe13421d856b2850a6ddb7/yarl-1.20.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:634b7ba6b4a85cf67e9df7c13a7fb2e44fa37b5d34501038d174a63eaac25ee2", size = 94647 }, + { url = "https://files.pythonhosted.org/packages/2c/29/8f291e7922a58a21349683f6120a85701aeefaa02e9f7c8a2dc24fe3f431/yarl-1.20.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d409e321e4addf7d97ee84162538c7258e53792eb7c6defd0c33647d754172e", size = 355788 }, + { url = "https://files.pythonhosted.org/packages/26/6d/b4892c80b805c42c228c6d11e03cafabf81662d371b0853e7f0f513837d5/yarl-1.20.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:ea52f7328a36960ba3231c6677380fa67811b414798a6e071c7085c57b6d20a9", size = 344613 }, + { url = "https://files.pythonhosted.org/packages/d7/0e/517aa28d3f848589bae9593717b063a544b86ba0a807d943c70f48fcf3bb/yarl-1.20.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c8703517b924463994c344dcdf99a2d5ce9eca2b6882bb640aa555fb5efc706a", size = 370953 }, + { url = "https://files.pythonhosted.org/packages/5f/9b/5bd09d2f1ad6e6f7c2beae9e50db78edd2cca4d194d227b958955573e240/yarl-1.20.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:077989b09ffd2f48fb2d8f6a86c5fef02f63ffe6b1dd4824c76de7bb01e4f2e2", size = 369204 }, + { url = "https://files.pythonhosted.org/packages/9c/85/d793a703cf4bd0d4cd04e4b13cc3d44149470f790230430331a0c1f52df5/yarl-1.20.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0acfaf1da020253f3533526e8b7dd212838fdc4109959a2c53cafc6db611bff2", size = 358108 }, + { url = "https://files.pythonhosted.org/packages/6f/54/b6c71e13549c1f6048fbc14ce8d930ac5fb8bafe4f1a252e621a24f3f1f9/yarl-1.20.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b4230ac0b97ec5eeb91d96b324d66060a43fd0d2a9b603e3327ed65f084e41f8", size = 346610 }, + { url = "https://files.pythonhosted.org/packages/a0/1a/d6087d58bdd0d8a2a37bbcdffac9d9721af6ebe50d85304d9f9b57dfd862/yarl-1.20.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0a6a1e6ae21cdd84011c24c78d7a126425148b24d437b5702328e4ba640a8902", size = 365378 }, + { url = "https://files.pythonhosted.org/packages/02/84/e25ddff4cbc001dbc4af76f8d41a3e23818212dd1f0a52044cbc60568872/yarl-1.20.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:86de313371ec04dd2531f30bc41a5a1a96f25a02823558ee0f2af0beaa7ca791", size = 356919 }, + { url = "https://files.pythonhosted.org/packages/04/76/898ae362353bf8f64636495d222c8014c8e5267df39b1a9fe1e1572fb7d0/yarl-1.20.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:dd59c9dd58ae16eaa0f48c3d0cbe6be8ab4dc7247c3ff7db678edecbaf59327f", size = 364248 }, + { url = "https://files.pythonhosted.org/packages/1b/b0/9d9198d83a622f1c40fdbf7bd13b224a6979f2e1fc2cf50bfb1d8773c495/yarl-1.20.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a0bc5e05f457b7c1994cc29e83b58f540b76234ba6b9648a4971ddc7f6aa52da", size = 378418 }, + { url = "https://files.pythonhosted.org/packages/c7/ce/1f50c1cc594cf5d3f5bf4a9b616fca68680deaec8ad349d928445ac52eb8/yarl-1.20.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:c9471ca18e6aeb0e03276b5e9b27b14a54c052d370a9c0c04a68cefbd1455eb4", size = 383850 }, + { url = "https://files.pythonhosted.org/packages/89/1e/a59253a87b35bfec1a25bb5801fb69943330b67cfd266278eb07e0609012/yarl-1.20.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:40ed574b4df723583a26c04b298b283ff171bcc387bc34c2683235e2487a65a5", size = 381218 }, + { url = "https://files.pythonhosted.org/packages/85/b0/26f87df2b3044b0ef1a7cf66d321102bdca091db64c5ae853fcb2171c031/yarl-1.20.0-cp311-cp311-win32.whl", hash = "sha256:db243357c6c2bf3cd7e17080034ade668d54ce304d820c2a58514a4e51d0cfd6", size = 86606 }, + { url = "https://files.pythonhosted.org/packages/33/46/ca335c2e1f90446a77640a45eeb1cd8f6934f2c6e4df7db0f0f36ef9f025/yarl-1.20.0-cp311-cp311-win_amd64.whl", hash = "sha256:8c12cd754d9dbd14204c328915e23b0c361b88f3cffd124129955e60a4fbfcfb", size = 93374 }, + { url = "https://files.pythonhosted.org/packages/c3/e8/3efdcb83073df978bb5b1a9cc0360ce596680e6c3fac01f2a994ccbb8939/yarl-1.20.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e06b9f6cdd772f9b665e5ba8161968e11e403774114420737f7884b5bd7bdf6f", size = 147089 }, + { url = "https://files.pythonhosted.org/packages/60/c3/9e776e98ea350f76f94dd80b408eaa54e5092643dbf65fd9babcffb60509/yarl-1.20.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b9ae2fbe54d859b3ade40290f60fe40e7f969d83d482e84d2c31b9bff03e359e", size = 97706 }, + { url = "https://files.pythonhosted.org/packages/0c/5b/45cdfb64a3b855ce074ae607b9fc40bc82e7613b94e7612b030255c93a09/yarl-1.20.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6d12b8945250d80c67688602c891237994d203d42427cb14e36d1a732eda480e", size = 95719 }, + { url = "https://files.pythonhosted.org/packages/2d/4e/929633b249611eeed04e2f861a14ed001acca3ef9ec2a984a757b1515889/yarl-1.20.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:087e9731884621b162a3e06dc0d2d626e1542a617f65ba7cc7aeab279d55ad33", size = 343972 }, + { url = "https://files.pythonhosted.org/packages/49/fd/047535d326c913f1a90407a3baf7ff535b10098611eaef2c527e32e81ca1/yarl-1.20.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:69df35468b66c1a6e6556248e6443ef0ec5f11a7a4428cf1f6281f1879220f58", size = 339639 }, + { url = "https://files.pythonhosted.org/packages/48/2f/11566f1176a78f4bafb0937c0072410b1b0d3640b297944a6a7a556e1d0b/yarl-1.20.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b2992fe29002fd0d4cbaea9428b09af9b8686a9024c840b8a2b8f4ea4abc16f", size = 353745 }, + { url = "https://files.pythonhosted.org/packages/26/17/07dfcf034d6ae8837b33988be66045dd52f878dfb1c4e8f80a7343f677be/yarl-1.20.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4c903e0b42aab48abfbac668b5a9d7b6938e721a6341751331bcd7553de2dcae", size = 354178 }, + { url = "https://files.pythonhosted.org/packages/15/45/212604d3142d84b4065d5f8cab6582ed3d78e4cc250568ef2a36fe1cf0a5/yarl-1.20.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf099e2432131093cc611623e0b0bcc399b8cddd9a91eded8bfb50402ec35018", size = 349219 }, + { url = "https://files.pythonhosted.org/packages/e6/e0/a10b30f294111c5f1c682461e9459935c17d467a760c21e1f7db400ff499/yarl-1.20.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8a7f62f5dc70a6c763bec9ebf922be52aa22863d9496a9a30124d65b489ea672", size = 337266 }, + { url = "https://files.pythonhosted.org/packages/33/a6/6efa1d85a675d25a46a167f9f3e80104cde317dfdf7f53f112ae6b16a60a/yarl-1.20.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:54ac15a8b60382b2bcefd9a289ee26dc0920cf59b05368c9b2b72450751c6eb8", size = 360873 }, + { url = "https://files.pythonhosted.org/packages/77/67/c8ab718cb98dfa2ae9ba0f97bf3cbb7d45d37f13fe1fbad25ac92940954e/yarl-1.20.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:25b3bc0763a7aca16a0f1b5e8ef0f23829df11fb539a1b70476dcab28bd83da7", size = 360524 }, + { url = "https://files.pythonhosted.org/packages/bd/e8/c3f18660cea1bc73d9f8a2b3ef423def8dadbbae6c4afabdb920b73e0ead/yarl-1.20.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b2586e36dc070fc8fad6270f93242124df68b379c3a251af534030a4a33ef594", size = 365370 }, + { url = "https://files.pythonhosted.org/packages/c9/99/33f3b97b065e62ff2d52817155a89cfa030a1a9b43fee7843ef560ad9603/yarl-1.20.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:866349da9d8c5290cfefb7fcc47721e94de3f315433613e01b435473be63daa6", size = 373297 }, + { url = "https://files.pythonhosted.org/packages/3d/89/7519e79e264a5f08653d2446b26d4724b01198a93a74d2e259291d538ab1/yarl-1.20.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:33bb660b390a0554d41f8ebec5cd4475502d84104b27e9b42f5321c5192bfcd1", size = 378771 }, + { url = "https://files.pythonhosted.org/packages/3a/58/6c460bbb884abd2917c3eef6f663a4a873f8dc6f498561fc0ad92231c113/yarl-1.20.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:737e9f171e5a07031cbee5e9180f6ce21a6c599b9d4b2c24d35df20a52fabf4b", size = 375000 }, + { url = "https://files.pythonhosted.org/packages/3b/2a/dd7ed1aa23fea996834278d7ff178f215b24324ee527df53d45e34d21d28/yarl-1.20.0-cp312-cp312-win32.whl", hash = "sha256:839de4c574169b6598d47ad61534e6981979ca2c820ccb77bf70f4311dd2cc64", size = 86355 }, + { url = "https://files.pythonhosted.org/packages/ca/c6/333fe0338305c0ac1c16d5aa7cc4841208d3252bbe62172e0051006b5445/yarl-1.20.0-cp312-cp312-win_amd64.whl", hash = "sha256:3d7dbbe44b443b0c4aa0971cb07dcb2c2060e4a9bf8d1301140a33a93c98e18c", size = 92904 }, + { url = "https://files.pythonhosted.org/packages/0f/6f/514c9bff2900c22a4f10e06297714dbaf98707143b37ff0bcba65a956221/yarl-1.20.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2137810a20b933b1b1b7e5cf06a64c3ed3b4747b0e5d79c9447c00db0e2f752f", size = 145030 }, + { url = "https://files.pythonhosted.org/packages/4e/9d/f88da3fa319b8c9c813389bfb3463e8d777c62654c7168e580a13fadff05/yarl-1.20.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:447c5eadd750db8389804030d15f43d30435ed47af1313303ed82a62388176d3", size = 96894 }, + { url = "https://files.pythonhosted.org/packages/cd/57/92e83538580a6968b2451d6c89c5579938a7309d4785748e8ad42ddafdce/yarl-1.20.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:42fbe577272c203528d402eec8bf4b2d14fd49ecfec92272334270b850e9cd7d", size = 94457 }, + { url = "https://files.pythonhosted.org/packages/e9/ee/7ee43bd4cf82dddd5da97fcaddb6fa541ab81f3ed564c42f146c83ae17ce/yarl-1.20.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18e321617de4ab170226cd15006a565d0fa0d908f11f724a2c9142d6b2812ab0", size = 343070 }, + { url = "https://files.pythonhosted.org/packages/4a/12/b5eccd1109e2097bcc494ba7dc5de156e41cf8309fab437ebb7c2b296ce3/yarl-1.20.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4345f58719825bba29895011e8e3b545e6e00257abb984f9f27fe923afca2501", size = 337739 }, + { url = "https://files.pythonhosted.org/packages/7d/6b/0eade8e49af9fc2585552f63c76fa59ef469c724cc05b29519b19aa3a6d5/yarl-1.20.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5d9b980d7234614bc4674468ab173ed77d678349c860c3af83b1fffb6a837ddc", size = 351338 }, + { url = "https://files.pythonhosted.org/packages/45/cb/aaaa75d30087b5183c7b8a07b4fb16ae0682dd149a1719b3a28f54061754/yarl-1.20.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:af4baa8a445977831cbaa91a9a84cc09debb10bc8391f128da2f7bd070fc351d", size = 353636 }, + { url = "https://files.pythonhosted.org/packages/98/9d/d9cb39ec68a91ba6e66fa86d97003f58570327d6713833edf7ad6ce9dde5/yarl-1.20.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:123393db7420e71d6ce40d24885a9e65eb1edefc7a5228db2d62bcab3386a5c0", size = 348061 }, + { url = "https://files.pythonhosted.org/packages/72/6b/103940aae893d0cc770b4c36ce80e2ed86fcb863d48ea80a752b8bda9303/yarl-1.20.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ab47acc9332f3de1b39e9b702d9c916af7f02656b2a86a474d9db4e53ef8fd7a", size = 334150 }, + { url = "https://files.pythonhosted.org/packages/ef/b2/986bd82aa222c3e6b211a69c9081ba46484cffa9fab2a5235e8d18ca7a27/yarl-1.20.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4a34c52ed158f89876cba9c600b2c964dfc1ca52ba7b3ab6deb722d1d8be6df2", size = 362207 }, + { url = "https://files.pythonhosted.org/packages/14/7c/63f5922437b873795d9422cbe7eb2509d4b540c37ae5548a4bb68fd2c546/yarl-1.20.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:04d8cfb12714158abf2618f792c77bc5c3d8c5f37353e79509608be4f18705c9", size = 361277 }, + { url = "https://files.pythonhosted.org/packages/81/83/450938cccf732466953406570bdb42c62b5ffb0ac7ac75a1f267773ab5c8/yarl-1.20.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7dc63ad0d541c38b6ae2255aaa794434293964677d5c1ec5d0116b0e308031f5", size = 364990 }, + { url = "https://files.pythonhosted.org/packages/b4/de/af47d3a47e4a833693b9ec8e87debb20f09d9fdc9139b207b09a3e6cbd5a/yarl-1.20.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f9d02b591a64e4e6ca18c5e3d925f11b559c763b950184a64cf47d74d7e41877", size = 374684 }, + { url = "https://files.pythonhosted.org/packages/62/0b/078bcc2d539f1faffdc7d32cb29a2d7caa65f1a6f7e40795d8485db21851/yarl-1.20.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:95fc9876f917cac7f757df80a5dda9de59d423568460fe75d128c813b9af558e", size = 382599 }, + { url = "https://files.pythonhosted.org/packages/74/a9/4fdb1a7899f1fb47fd1371e7ba9e94bff73439ce87099d5dd26d285fffe0/yarl-1.20.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:bb769ae5760cd1c6a712135ee7915f9d43f11d9ef769cb3f75a23e398a92d384", size = 378573 }, + { url = "https://files.pythonhosted.org/packages/fd/be/29f5156b7a319e4d2e5b51ce622b4dfb3aa8d8204cd2a8a339340fbfad40/yarl-1.20.0-cp313-cp313-win32.whl", hash = "sha256:70e0c580a0292c7414a1cead1e076c9786f685c1fc4757573d2967689b370e62", size = 86051 }, + { url = "https://files.pythonhosted.org/packages/52/56/05fa52c32c301da77ec0b5f63d2d9605946fe29defacb2a7ebd473c23b81/yarl-1.20.0-cp313-cp313-win_amd64.whl", hash = "sha256:4c43030e4b0af775a85be1fa0433119b1565673266a70bf87ef68a9d5ba3174c", size = 92742 }, + { url = "https://files.pythonhosted.org/packages/d4/2f/422546794196519152fc2e2f475f0e1d4d094a11995c81a465faf5673ffd/yarl-1.20.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b6c4c3d0d6a0ae9b281e492b1465c72de433b782e6b5001c8e7249e085b69051", size = 163575 }, + { url = "https://files.pythonhosted.org/packages/90/fc/67c64ddab6c0b4a169d03c637fb2d2a212b536e1989dec8e7e2c92211b7f/yarl-1.20.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:8681700f4e4df891eafa4f69a439a6e7d480d64e52bf460918f58e443bd3da7d", size = 106121 }, + { url = "https://files.pythonhosted.org/packages/6d/00/29366b9eba7b6f6baed7d749f12add209b987c4cfbfa418404dbadc0f97c/yarl-1.20.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:84aeb556cb06c00652dbf87c17838eb6d92cfd317799a8092cee0e570ee11229", size = 103815 }, + { url = "https://files.pythonhosted.org/packages/28/f4/a2a4c967c8323c03689383dff73396281ced3b35d0ed140580825c826af7/yarl-1.20.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f166eafa78810ddb383e930d62e623d288fb04ec566d1b4790099ae0f31485f1", size = 408231 }, + { url = "https://files.pythonhosted.org/packages/0f/a1/66f7ffc0915877d726b70cc7a896ac30b6ac5d1d2760613603b022173635/yarl-1.20.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:5d3d6d14754aefc7a458261027a562f024d4f6b8a798adb472277f675857b1eb", size = 390221 }, + { url = "https://files.pythonhosted.org/packages/41/15/cc248f0504610283271615e85bf38bc014224122498c2016d13a3a1b8426/yarl-1.20.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2a8f64df8ed5d04c51260dbae3cc82e5649834eebea9eadfd829837b8093eb00", size = 411400 }, + { url = "https://files.pythonhosted.org/packages/5c/af/f0823d7e092bfb97d24fce6c7269d67fcd1aefade97d0a8189c4452e4d5e/yarl-1.20.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4d9949eaf05b4d30e93e4034a7790634bbb41b8be2d07edd26754f2e38e491de", size = 411714 }, + { url = "https://files.pythonhosted.org/packages/83/70/be418329eae64b9f1b20ecdaac75d53aef098797d4c2299d82ae6f8e4663/yarl-1.20.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c366b254082d21cc4f08f522ac201d0d83a8b8447ab562732931d31d80eb2a5", size = 404279 }, + { url = "https://files.pythonhosted.org/packages/19/f5/52e02f0075f65b4914eb890eea1ba97e6fd91dd821cc33a623aa707b2f67/yarl-1.20.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:91bc450c80a2e9685b10e34e41aef3d44ddf99b3a498717938926d05ca493f6a", size = 384044 }, + { url = "https://files.pythonhosted.org/packages/6a/36/b0fa25226b03d3f769c68d46170b3e92b00ab3853d73127273ba22474697/yarl-1.20.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9c2aa4387de4bc3a5fe158080757748d16567119bef215bec643716b4fbf53f9", size = 416236 }, + { url = "https://files.pythonhosted.org/packages/cb/3a/54c828dd35f6831dfdd5a79e6c6b4302ae2c5feca24232a83cb75132b205/yarl-1.20.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:d2cbca6760a541189cf87ee54ff891e1d9ea6406079c66341008f7ef6ab61145", size = 402034 }, + { url = "https://files.pythonhosted.org/packages/10/97/c7bf5fba488f7e049f9ad69c1b8fdfe3daa2e8916b3d321aa049e361a55a/yarl-1.20.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:798a5074e656f06b9fad1a162be5a32da45237ce19d07884d0b67a0aa9d5fdda", size = 407943 }, + { url = "https://files.pythonhosted.org/packages/fd/a4/022d2555c1e8fcff08ad7f0f43e4df3aba34f135bff04dd35d5526ce54ab/yarl-1.20.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:f106e75c454288472dbe615accef8248c686958c2e7dd3b8d8ee2669770d020f", size = 423058 }, + { url = "https://files.pythonhosted.org/packages/4c/f6/0873a05563e5df29ccf35345a6ae0ac9e66588b41fdb7043a65848f03139/yarl-1.20.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:3b60a86551669c23dc5445010534d2c5d8a4e012163218fc9114e857c0586fdd", size = 423792 }, + { url = "https://files.pythonhosted.org/packages/9e/35/43fbbd082708fa42e923f314c24f8277a28483d219e049552e5007a9aaca/yarl-1.20.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:3e429857e341d5e8e15806118e0294f8073ba9c4580637e59ab7b238afca836f", size = 422242 }, + { url = "https://files.pythonhosted.org/packages/ed/f7/f0f2500cf0c469beb2050b522c7815c575811627e6d3eb9ec7550ddd0bfe/yarl-1.20.0-cp313-cp313t-win32.whl", hash = "sha256:65a4053580fe88a63e8e4056b427224cd01edfb5f951498bfefca4052f0ce0ac", size = 93816 }, + { url = "https://files.pythonhosted.org/packages/3f/93/f73b61353b2a699d489e782c3f5998b59f974ec3156a2050a52dfd7e8946/yarl-1.20.0-cp313-cp313t-win_amd64.whl", hash = "sha256:53b2da3a6ca0a541c1ae799c349788d480e5144cac47dba0266c7cb6c76151fe", size = 101093 }, + { url = "https://files.pythonhosted.org/packages/ea/1f/70c57b3d7278e94ed22d85e09685d3f0a38ebdd8c5c73b65ba4c0d0fe002/yarl-1.20.0-py3-none-any.whl", hash = "sha256:5d0fe6af927a47a230f31e6004621fd0959eaa915fc62acfafa67ff7229a3124", size = 46124 }, +] From 18aa4dab1c031863c1aa874315ab6e98b8a4d6f4 Mon Sep 17 00:00:00 2001 From: GitHub CI Date: Tue, 29 Jul 2025 17:35:43 +0200 Subject: [PATCH 5/9] A working version Signed-off-by: GitHub CI --- .../stamp/app/config/config_fed_client.conf | 10 +- application/jobs/stamp/app/custom/data.py | 9 - application/jobs/stamp/app/custom/main.py | 218 ++- .../jobs/stamp/app/custom/modeling/alibi.py | 147 ++ .../app/custom/modeling/lightning_model.py | 95 +- .../app/custom/modeling/mlp_classifier.py | 123 ++ .../jobs/stamp/app/custom/modeling/module.py | 1654 +++++++++++++++++ .../stamp/app/custom/modeling/registry.py | 34 + .../app/custom/modeling/vision_transformer.py | 3 +- 9 files changed, 2198 insertions(+), 95 deletions(-) create mode 100644 application/jobs/stamp/app/custom/modeling/alibi.py create mode 100644 application/jobs/stamp/app/custom/modeling/mlp_classifier.py create mode 100644 application/jobs/stamp/app/custom/modeling/module.py create mode 100644 application/jobs/stamp/app/custom/modeling/registry.py diff --git a/application/jobs/stamp/app/config/config_fed_client.conf b/application/jobs/stamp/app/config/config_fed_client.conf index 5565b668..826af222 100644 --- a/application/jobs/stamp/app/config/config_fed_client.conf +++ b/application/jobs/stamp/app/config/config_fed_client.conf @@ -84,11 +84,19 @@ model { path = "modeling.lightning_model.LitVisionTransformer" args { + categories = ['WT', 'MUT'] + dim_input = 1536 + category_weights =[0.8676, 0.1324] dim_model = 512 - dim_feedforward=2048 + dim_feedforward=512 n_heads=8 n_layers=2 dropout=0.25 +use_alibi=False + ground_truth_label='isMSIH' + train_patients=['TCGA-AH-6644', 'TCGA-AA-3664', 'TCGA-AZ-4614', 'TCGA-D5-5537', 'TCGA-G4-6628', 'TCGA-EI-6882', 'TCGA-AF-2689', 'TCGA-F4-6805', 'TCGA-AZ-4681', 'TCGA-AG-3882', 'TCGA-A6-6648', 'TCGA-AA-A017', 'TCGA-G4-6303', 'TCGA-EI-6508', 'TCGA-AF-6136', 'TCGA-A6-6654', 'TCGA-AD-6895', 'TCGA-CM-5860', 'TCGA-A6-4105', 'TCGA-F5-6810', 'TCGA-QG-A5YV', 'TCGA-DC-6155', 'TCGA-F5-6812', 'TCGA-QL-A97D', 'TCGA-AZ-4615', 'TCGA-CK-6751', 'TCGA-AH-6547', 'TCGA-AA-3980', 'TCGA-CL-5918', 'TCGA-DM-A282', 'TCGA-AA-3514', 'TCGA-EI-6509', 'TCGA-AF-3911', 'TCGA-CL-5917', 'TCGA-A6-2686', 'TCGA-AA-3950', 'TCGA-AA-3549', 'TCGA-AA-3973', 'TCGA-D5-6922', 'TCGA-AG-3887', 'TCGA-CA-6717', 'TCGA-CM-6171', 'TCGA-AD-6964', 'TCGA-A6-6142', 'TCGA-AD-6890', 'TCGA-A6-6138', 'TCGA-AD-A5EK', 'TCGA-D5-6929', 'TCGA-G4-6588', 'TCGA-AZ-4616', 'TCGA-AA-3561', 'TCGA-QG-A5Z2', 'TCGA-DM-A1HA', 'TCGA-CL-4957', 'TCGA-CA-5255', 'TCGA-DC-6158', 'TCGA-G4-6295', 'TCGA-AY-A69D', 'TCGA-DM-A1D6', 'TCGA-AG-3896', 'TCGA-A6-6652', 'TCGA-CM-6680', 'TCGA-AA-3821', 'TCGA-D5-5540', 'TCGA-DC-5869', 'TCGA-AG-4008', 'TCGA-CK-4952', 'TCGA-AA-3875', 'TCGA-AA-A02W', 'TCGA-EI-6514', 'TCGA-DM-A288', 'TCGA-AG-3890', 'TCGA-AA-3979', 'TCGA-NH-A5IV', 'TCGA-G4-6297', 'TCGA-D5-6927', 'TCGA-AA-3680', 'TCGA-NH-A8F8', 'TCGA-A6-5662', 'TCGA-A6-2674', 'TCGA-AG-A01N', 'TCGA-DM-A1D8', 'TCGA-AA-3837', 'TCGA-AA-A01I', 'TCGA-DM-A0XF', 'TCGA-F4-6855', 'TCGA-CA-6715', 'TCGA-AA-3975', 'TCGA-AA-3989', 'TCGA-AG-A016', 'TCGA-A6-5666', 'TCGA-CK-6746', 'TCGA-CM-4752', 'TCGA-CM-6676', 'TCGA-D5-6541', 'TCGA-A6-5667', 'TCGA-AF-2691', 'TCGA-AD-A5EJ', 'TCGA-F5-6864', 'TCGA-AG-3726', 'TCGA-A6-6649', 'TCGA-AA-A03J', 'TCGA-AF-2687', 'TCGA-AA-3858', 'TCGA-G4-6321', 'TCGA-G5-6641', 'TCGA-G4-6309', 'TCGA-D5-5538', 'TCGA-DM-A28K', 'TCGA-AG-3878', 'TCGA-AZ-6598', 'TCGA-AA-3688', 'TCGA-AG-A01W', 'TCGA-AA-3517', 'TCGA-D5-6898', 'TCGA-CM-5341', 'TCGA-CM-6167', 'TCGA-D5-6531', 'TCGA-DC-6683', 'TCGA-AF-2690', 'TCGA-CM-6161', 'TCGA-A6-5660', 'TCGA-NH-A8F7', 'TCGA-CM-6163', 'TCGA-AA-3968', 'TCGA-CM-5868', 'TCGA-AA-3976', 'TCGA-A6-A565', 'TCGA-AM-5821', 'TCGA-AD-6965', 'TCGA-AY-A71X', 'TCGA-AZ-4682', 'TCGA-AG-3892', 'TCGA-A6-2675', 'TCGA-F4-6569', 'TCGA-AG-3893', 'TCGA-CM-5862', 'TCGA-AZ-6606', 'TCGA-CM-6678', 'TCGA-D5-6931', 'TCGA-D5-6923', 'TCGA-AG-3575', 'TCGA-AA-A00Z', 'TCGA-D5-6540', 'TCGA-A6-3808', 'TCGA-CM-4743', 'TCGA-DC-4749', 'TCGA-T9-A92H', 'TCGA-A6-3810', 'TCGA-AD-6889', 'TCGA-G4-6320', 'TCGA-AA-3971', 'TCGA-AZ-4308', 'TCGA-D5-7000', 'TCGA-DM-A28A', 'TCGA-AG-A01L', 'TCGA-EI-6512', 'TCGA-A6-5661', 'TCGA-AG-3599', 'TCGA-AG-3902', 'TCGA-AD-6548', 'TCGA-AA-A022', 'TCGA-AG-3580', 'TCGA-A6-A567', 'TCGA-AA-3679', 'TCGA-CA-5256', 'TCGA-F4-6808', 'TCGA-D5-6536', 'TCGA-DM-A1D9', 'TCGA-AG-3885', 'TCGA-DM-A1D0', 'TCGA-CM-6674', 'TCGA-EI-6507', 'TCGA-F4-6459', 'TCGA-A6-6137', 'TCGA-AD-6899', 'TCGA-EI-6885', 'TCGA-WS-AB45', 'TCGA-D5-6930', 'TCGA-AU-6004', 'TCGA-AY-6196', 'TCGA-AA-A010', 'TCGA-AG-A00C', 'TCGA-4T-AA8H', 'TCGA-G4-6302', 'TCGA-AA-3966', 'TCGA-AF-2693', 'TCGA-D5-6926', 'TCGA-DM-A0XD', 'TCGA-AA-3854', 'TCGA-D5-6932', 'TCGA-EI-7004', 'TCGA-AG-3594', 'TCGA-EI-6510', 'TCGA-AA-3715', 'TCGA-AA-A01X', 'TCGA-F4-6570', 'TCGA-EI-6511', 'TCGA-CM-5861', 'TCGA-CA-5254', 'TCGA-G4-6317', 'TCGA-DM-A28M', 'TCGA-EI-6506', 'TCGA-AG-A020', 'TCGA-AG-3583', 'TCGA-G4-6294', 'TCGA-CM-6165', 'TCGA-D5-6535', 'TCGA-D5-5541', 'TCGA-DM-A1DB', 'TCGA-AG-A01J', 'TCGA-AG-4001', 'TCGA-AG-A00Y', 'TCGA-AA-3949', 'TCGA-AA-3842', 'TCGA-CA-6716', 'TCGA-CM-5348', 'TCGA-AA-3696', 'TCGA-AA-3833', 'TCGA-NH-A6GC', 'TCGA-CK-4947', 'TCGA-AA-3846', 'TCGA-A6-2677', 'TCGA-A6-2684', 'TCGA-NH-A6GA', 'TCGA-F5-6811', 'TCGA-A6-4107', 'TCGA-F5-6571', 'TCGA-CK-6747', 'TCGA-AA-3666', 'TCGA-CK-5914', 'TCGA-G4-6627', 'TCGA-DC-6681', 'TCGA-AZ-4315', 'TCGA-DT-5265', 'TCGA-CM-4751', 'TCGA-CM-6168', 'TCGA-AY-5543', 'TCGA-AZ-6607', 'TCGA-AG-3901', 'TCGA-AA-3695', 'TCGA-AD-6963', 'TCGA-CK-5913', 'TCGA-AF-2692', 'TCGA-G4-6307', 'TCGA-AA-A01T', 'TCGA-DY-A1DG', 'TCGA-AA-A01C', 'TCGA-AZ-6603', 'TCGA-AG-3727', 'TCGA-AG-3909', 'TCGA-D5-6539', 'TCGA-AZ-5407', 'TCGA-NH-A6GB', 'TCGA-AA-3851', 'TCGA-AF-5654', 'TCGA-AA-3530', 'TCGA-F5-6813', 'TCGA-AA-3845', 'TCGA-AG-3584', 'TCGA-AM-5820', 'TCGA-AA-3982', 'TCGA-D5-6924', 'TCGA-AA-3984', 'TCGA-5M-AATE', 'TCGA-AG-3894', 'TCGA-AU-3779', 'TCGA-A6-2671', 'TCGA-DC-6682', 'TCGA-A6-6653', 'TCGA-DM-A28E', 'TCGA-AZ-5403', 'TCGA-CK-4948', 'TCGA-AF-6655', 'TCGA-AA-A01P', 'TCGA-AA-A024', 'TCGA-F4-6809', 'TCGA-AA-3548', 'TCGA-QG-A5YW', 'TCGA-AA-3818', 'TCGA-EI-6513', 'TCGA-AY-4071', 'TCGA-D5-6532', 'TCGA-AZ-6600', 'TCGA-CM-6172', 'TCGA-AG-A01Y', 'TCGA-AA-3675', 'TCGA-AD-6888', 'TCGA-AH-6897', 'TCGA-AA-3852', 'TCGA-CM-4747', 'TCGA-AG-A02N', 'TCGA-AA-A02F', 'TCGA-DM-A1D7', 'TCGA-AA-3678', 'TCGA-CM-6677', 'TCGA-AA-A004', 'TCGA-D5-5539', 'TCGA-F5-6861', 'TCGA-AA-3544', 'TCGA-F4-6463', 'TCGA-AG-4015', 'TCGA-AF-3400', 'TCGA-CM-6169', 'TCGA-F4-6461', 'TCGA-AD-6901', 'TCGA-DM-A280', 'TCGA-AA-3819', 'TCGA-DM-A0X9', 'TCGA-G4-6315', 'TCGA-AA-3850', 'TCGA-CA-5797', 'TCGA-AA-3856', 'TCGA-AY-6386', 'TCGA-AG-3598', 'TCGA-CM-5863', 'TCGA-AG-3605', 'TCGA-AZ-6599', 'TCGA-CM-5344', 'TCGA-AA-3693', 'TCGA-G4-6306', 'TCGA-F4-6854', 'TCGA-A6-5656', 'TCGA-DY-A0XA', 'TCGA-A6-2685', 'TCGA-AY-6197', 'TCGA-4N-A93T', 'TCGA-NH-A50U', 'TCGA-A6-2683', 'TCGA-AA-3952', 'TCGA-DC-6160', 'TCGA-A6-3807', 'TCGA-AA-3522', 'TCGA-G4-6304', 'TCGA-AA-3526', 'TCGA-DC-4745', 'TCGA-AA-3877', 'TCGA-CM-6162', 'TCGA-AA-3692', 'TCGA-AA-3524', 'TCGA-AG-3881', 'TCGA-AA-3681', 'TCGA-CK-4951', 'TCGA-AA-3534', 'TCGA-A6-A56B', 'TCGA-DM-A1D4', 'TCGA-5M-AAT5', 'TCGA-AG-4022', 'TCGA-AA-3812', 'TCGA-D5-6529', 'TCGA-AA-3673', 'TCGA-EI-6917', 'TCGA-EI-6884', 'TCGA-AG-A011', 'TCGA-AY-A8YK', 'TCGA-EI-6881', 'TCGA-A6-2678', 'TCGA-AA-3947', 'TCGA-A6-5659', 'TCGA-AA-3994', 'TCGA-D5-6534', 'TCGA-AG-3574', 'TCGA-F5-6465', 'TCGA-CA-6719', 'TCGA-AA-3532', 'TCGA-G4-6322', 'TCGA-AF-A56K', 'TCGA-AA-3864', 'TCGA-AA-3685', 'TCGA-AA-3986'] + valid_patients=['TCGA-AG-4021', 'TCGA-AG-A02X', 'TCGA-AA-A02H', 'TCGA-F4-6807', 'TCGA-G4-6311', 'TCGA-AF-A56L', 'TCGA-CM-4748', 'TCGA-AZ-6605', 'TCGA-G4-6299', 'TCGA-AA-3667', 'TCGA-5M-AAT4', 'TCGA-5M-AAT6', 'TCGA-F4-6704', 'TCGA-AH-6544', 'TCGA-3L-AA1B', 'TCGA-CK-4950', 'TCGA-AA-3956', 'TCGA-G4-6323', 'TCGA-AA-3831', 'TCGA-AH-6903', 'TCGA-CA-5796', 'TCGA-AA-3684', 'TCGA-EI-7002', 'TCGA-F4-6460', 'TCGA-AA-3844', 'TCGA-AA-3848', 'TCGA-D5-6928', 'TCGA-A6-5665', 'TCGA-AA-A01S', 'TCGA-DM-A28H', 'TCGA-DC-6157', 'TCGA-DY-A1DD', 'TCGA-AA-3519', 'TCGA-AA-3855', 'TCGA-F4-6806', 'TCGA-AA-3520', 'TCGA-CM-6675', 'TCGA-AG-3898', 'TCGA-CK-6748', 'TCGA-G4-6298', 'TCGA-G4-6626', 'TCGA-QG-A5Z1', 'TCGA-DM-A28G', 'TCGA-A6-2681', 'TCGA-DM-A28F', 'TCGA-G4-6586', 'TCGA-CK-5916', 'TCGA-AG-A002', 'TCGA-AG-A026', 'TCGA-AA-A02E', 'TCGA-F5-6464', 'TCGA-EI-6883', 'TCGA-F4-6856', 'TCGA-AG-A015', 'TCGA-CM-5349', 'TCGA-DM-A1DA', 'TCGA-D5-6530', 'TCGA-A6-A566', 'TCGA-NH-A50V', 'TCGA-F5-6814', 'TCGA-CM-6166', 'TCGA-CI-6622', 'TCGA-AA-3977', 'TCGA-CA-6718', 'TCGA-AA-3841', 'TCGA-AA-3521', 'TCGA-NH-A50T', 'TCGA-AA-A01R', 'TCGA-AG-A008', 'TCGA-CM-4746', 'TCGA-AA-A02R', 'TCGA-DC-6154', 'TCGA-AA-3531', 'TCGA-F5-6863', 'TCGA-F4-6703', 'TCGA-AY-A54L', 'TCGA-AA-3811', 'TCGA-AA-3814', 'TCGA-CK-5912', 'TCGA-A6-3809', 'TCGA-A6-5657', 'TCGA-EF-5830', 'TCGA-CM-6164', 'TCGA-SS-A7HO', 'TCGA-CM-5864', 'TCGA-AF-4110', 'TCGA-AF-6672', 'TCGA-AA-3866', 'TCGA-AA-A01V', 'TCGA-AA-A00N', 'TCGA-D5-6538', 'TCGA-CM-6170', 'TCGA-D5-6537', 'TCGA-AA-A02Y', 'TCGA-DY-A1DC', 'TCGA-AG-3883', 'TCGA-AD-5900', 'TCGA-AA-3955', 'TCGA-AG-3581', 'TCGA-A6-5664', 'TCGA-DY-A1DF', 'TCGA-AA-A02O', 'TCGA-AA-3527', 'TCGA-F5-6702', 'TCGA-AH-6643', 'TCGA-G4-6293', 'TCGA-DC-5337', 'TCGA-AA-A03F', 'TCGA-CI-6624', 'TCGA-AG-3602', 'TCGA-AA-3538', 'TCGA-AA-A01Z', 'TCGA-AA-3558', 'TCGA-AA-3560', 'TCGA-AA-3867', 'TCGA-A6-6650', 'TCGA-CK-5915', 'TCGA-A6-6651', 'TCGA-AY-4070', 'TCGA-AF-A56N', 'TCGA-D5-6533', 'TCGA-AA-3529', 'TCGA-AG-A032', 'TCGA-QG-A5YX'] + } } } diff --git a/application/jobs/stamp/app/custom/data.py b/application/jobs/stamp/app/custom/data.py index f8d4482b..b7f51b0e 100755 --- a/application/jobs/stamp/app/custom/data.py +++ b/application/jobs/stamp/app/custom/data.py @@ -10,10 +10,8 @@ import h5py import numpy as np import pandas as pd -import stamp import torch from jaxtyping import Bool, Float -from packaging.version import Version from torch import Tensor from torch.utils.data import DataLoader, Dataset @@ -230,13 +228,6 @@ def get_coords(feature_h5: h5py.File) -> CoordsInfo: tile_size_px = TilePixels(224) coords_um = coords / 224 * 256 - if (version_str := feature_h5.attrs.get("stamp_version")) and ( - extraction_version := Version(version_str) - ) > Version(stamp.__version__): - raise RuntimeError( - f"features were extracted with a newer version of stamp, please update your stamp to at least version {extraction_version}." - ) - if not tile_size_px and "tile_size_px" in feature_h5.attrs: tile_size_px = TilePixels(int(feature_h5.attrs["tile_size_px"])) # pyright: ignore[reportArgumentType] diff --git a/application/jobs/stamp/app/custom/main.py b/application/jobs/stamp/app/custom/main.py index cf85e048..18f73398 100644 --- a/application/jobs/stamp/app/custom/main.py +++ b/application/jobs/stamp/app/custom/main.py @@ -1,7 +1,14 @@ import logging import sys -logger = logging.getLogger("stamp") +from torch import tensor + +import modeling.lightning_model +from modeling.registry import MODEL_REGISTRY + +print("Training script is using:", modeling.lightning_model.__file__) + +logger = logging.getLogger("test") logger.setLevel(logging.DEBUG) if not logger.hasHandlers(): @@ -23,6 +30,7 @@ from lightning.pytorch.accelerators.accelerator import Accelerator from lightning.pytorch.callbacks import ModelCheckpoint from lightning.pytorch.loggers import CSVLogger + from sklearn.model_selection import train_test_split from torch.utils.data.dataloader import DataLoader import logging @@ -152,13 +160,13 @@ def train_categorical_model_( def train_model_( *, output_dir: Path, - model: LitVisionTransformer, + model: lightning.LightningModule, train_dl: DataLoader[tuple[Bags, CoordinatesBatch, BagSizes, EncodedTargets]], valid_dl: DataLoader[tuple[Bags, CoordinatesBatch, BagSizes, EncodedTargets]], max_epochs: int, patience: int, accelerator: str | Accelerator, -) -> LitVisionTransformer: +) -> lightning.LightningModule: """Trains a model. Returns: @@ -177,19 +185,12 @@ def train_model_( # EarlyStopping(monitor="validation_loss", mode="min", patience=patience), model_checkpoint, ], - max_epochs=max_epochs, - # FIXME The number of accelerators is currently fixed to one for the - # following reasons: - # 1. `trainer.predict()` does not return any predictions if used with - # the default strategy no multiple GPUs - # 2. `barspoon.model.SafeMulticlassAUROC` breaks on multiple GPUs accelerator=accelerator, devices=1, gradient_clip_val=0.5, logger=CSVLogger(save_dir=output_dir), log_every_n_steps=len(train_dl), - # num_sanity_val_steps=0, ) flare_util.init() SITE_NAME = flare.get_site_name() @@ -200,12 +201,15 @@ def train_model_( while flare.is_running(): logger.info("[DEBUG] waiting to receive swarm model (10s timeout)") - input_model = flare.receive(timeout=10) - + input_model = flare.receive() + # logger.info('ModelClass state dict keys()', model.state_dict().keys()) + ''' if input_model is not None: logger.info("==== Swarm model received ====") logger.info( f"input_model.params.keys() = {list(input_model.params.keys())[:10]} ... total = {len(input_model.params)}") + # log input_model.params + logger.info(f"input_model.params.keys() = {input_model.params.keys()}") logger.info( f"model.state_dict().keys() = {list(model.state_dict().keys())[:10]} ... total = {len(model.state_dict())}") try: @@ -222,6 +226,15 @@ def train_model_( logger.info("[DEBUG] received swarm_start:", input_model) logger.info(f"Current round: {input_model.current_round}") + ''' + + logger.info(f"input_model.params.keys() = {input_model.params.keys()}") + logger.info( + f"model.state_dict().keys() = {list(model.state_dict().keys())[:10]} ... total = {len(model.state_dict())}") + # log type(model) + logger.info(f"model class: {type(model)}") + + #model = input_model.model trainer.fit( model=model, @@ -248,7 +261,7 @@ def setup_model_for_training( slide_table: Path, feature_dir: Path, ) -> tuple[ - LitVisionTransformer, + lightning.LightningModule, DataLoader[tuple[Bags, CoordinatesBatch, BagSizes, EncodedTargets]], DataLoader[tuple[Bags, CoordinatesBatch, BagSizes, EncodedTargets]], ]: @@ -322,25 +335,182 @@ def setup_model_for_training( ) # Train the model - - model = LitVisionTransformer(dim_model=512, - dim_feedforward=2048, + model_info = MODEL_REGISTRY['vit'] + + ModelClass = model_info["model_class"] + ''' + categories = ['WT', 'MUT'] + dim_input = 1536 + category_weights =[0.8676, 0.1324] + dim_model = 512 + dim_feedforward=512 + n_heads=8 + n_layers=2 + dropout=0.25 +use_alibi=False + ground_truth_label='isMSIH' + train_patients=['TCGA-AH-6644', 'TCGA-AA-3664', 'TCGA-AZ-4614', 'TCGA-D5-5537', 'TCGA-G4-6628', 'TCGA-EI-6882', 'TCGA-AF-2689', 'TCGA-F4-6805', 'TCGA-AZ-4681', 'TCGA-AG-3882', 'TCGA-A6-6648', 'TCGA-AA-A017', 'TCGA-G4-6303', 'TCGA-EI-6508', 'TCGA-AF-6136', 'TCGA-A6-6654', 'TCGA-AD-6895', 'TCGA-CM-5860', 'TCGA-A6-4105', 'TCGA-F5-6810', 'TCGA-QG-A5YV', 'TCGA-DC-6155', 'TCGA-F5-6812', 'TCGA-QL-A97D', 'TCGA-AZ-4615', 'TCGA-CK-6751', 'TCGA-AH-6547', 'TCGA-AA-3980', 'TCGA-CL-5918', 'TCGA-DM-A282', 'TCGA-AA-3514', 'TCGA-EI-6509', 'TCGA-AF-3911', 'TCGA-CL-5917', 'TCGA-A6-2686', 'TCGA-AA-3950', 'TCGA-AA-3549', 'TCGA-AA-3973', 'TCGA-D5-6922', 'TCGA-AG-3887', 'TCGA-CA-6717', 'TCGA-CM-6171', 'TCGA-AD-6964', 'TCGA-A6-6142', 'TCGA-AD-6890', 'TCGA-A6-6138', 'TCGA-AD-A5EK', 'TCGA-D5-6929', 'TCGA-G4-6588', 'TCGA-AZ-4616', 'TCGA-AA-3561', 'TCGA-QG-A5Z2', 'TCGA-DM-A1HA', 'TCGA-CL-4957', 'TCGA-CA-5255', 'TCGA-DC-6158', 'TCGA-G4-6295', 'TCGA-AY-A69D', 'TCGA-DM-A1D6', 'TCGA-AG-3896', 'TCGA-A6-6652', 'TCGA-CM-6680', 'TCGA-AA-3821', 'TCGA-D5-5540', 'TCGA-DC-5869', 'TCGA-AG-4008', 'TCGA-CK-4952', 'TCGA-AA-3875', 'TCGA-AA-A02W', 'TCGA-EI-6514', 'TCGA-DM-A288', 'TCGA-AG-3890', 'TCGA-AA-3979', 'TCGA-NH-A5IV', 'TCGA-G4-6297', 'TCGA-D5-6927', 'TCGA-AA-3680', 'TCGA-NH-A8F8', 'TCGA-A6-5662', 'TCGA-A6-2674', 'TCGA-AG-A01N', 'TCGA-DM-A1D8', 'TCGA-AA-3837', 'TCGA-AA-A01I', 'TCGA-DM-A0XF', 'TCGA-F4-6855', 'TCGA-CA-6715', 'TCGA-AA-3975', 'TCGA-AA-3989', 'TCGA-AG-A016', 'TCGA-A6-5666', 'TCGA-CK-6746', 'TCGA-CM-4752', 'TCGA-CM-6676', 'TCGA-D5-6541', 'TCGA-A6-5667', 'TCGA-AF-2691', 'TCGA-AD-A5EJ', 'TCGA-F5-6864', 'TCGA-AG-3726', 'TCGA-A6-6649', 'TCGA-AA-A03J', 'TCGA-AF-2687', 'TCGA-AA-3858', 'TCGA-G4-6321', 'TCGA-G5-6641', 'TCGA-G4-6309', 'TCGA-D5-5538', 'TCGA-DM-A28K', 'TCGA-AG-3878', 'TCGA-AZ-6598', 'TCGA-AA-3688', 'TCGA-AG-A01W', 'TCGA-AA-3517', 'TCGA-D5-6898', 'TCGA-CM-5341', 'TCGA-CM-6167', 'TCGA-D5-6531', 'TCGA-DC-6683', 'TCGA-AF-2690', 'TCGA-CM-6161', 'TCGA-A6-5660', 'TCGA-NH-A8F7', 'TCGA-CM-6163', 'TCGA-AA-3968', 'TCGA-CM-5868', 'TCGA-AA-3976', 'TCGA-A6-A565', 'TCGA-AM-5821', 'TCGA-AD-6965', 'TCGA-AY-A71X', 'TCGA-AZ-4682', 'TCGA-AG-3892', 'TCGA-A6-2675', 'TCGA-F4-6569', 'TCGA-AG-3893', 'TCGA-CM-5862', 'TCGA-AZ-6606', 'TCGA-CM-6678', 'TCGA-D5-6931', 'TCGA-D5-6923', 'TCGA-AG-3575', 'TCGA-AA-A00Z', 'TCGA-D5-6540', 'TCGA-A6-3808', 'TCGA-CM-4743', 'TCGA-DC-4749', 'TCGA-T9-A92H', 'TCGA-A6-3810', 'TCGA-AD-6889', 'TCGA-G4-6320', 'TCGA-AA-3971', 'TCGA-AZ-4308', 'TCGA-D5-7000', 'TCGA-DM-A28A', 'TCGA-AG-A01L', 'TCGA-EI-6512', 'TCGA-A6-5661', 'TCGA-AG-3599', 'TCGA-AG-3902', 'TCGA-AD-6548', 'TCGA-AA-A022', 'TCGA-AG-3580', 'TCGA-A6-A567', 'TCGA-AA-3679', 'TCGA-CA-5256', 'TCGA-F4-6808', 'TCGA-D5-6536', 'TCGA-DM-A1D9', 'TCGA-AG-3885', 'TCGA-DM-A1D0', 'TCGA-CM-6674', 'TCGA-EI-6507', 'TCGA-F4-6459', 'TCGA-A6-6137', 'TCGA-AD-6899', 'TCGA-EI-6885', 'TCGA-WS-AB45', 'TCGA-D5-6930', 'TCGA-AU-6004', 'TCGA-AY-6196', 'TCGA-AA-A010', 'TCGA-AG-A00C', 'TCGA-4T-AA8H', 'TCGA-G4-6302', 'TCGA-AA-3966', 'TCGA-AF-2693', 'TCGA-D5-6926', 'TCGA-DM-A0XD', 'TCGA-AA-3854', 'TCGA-D5-6932', 'TCGA-EI-7004', 'TCGA-AG-3594', 'TCGA-EI-6510', 'TCGA-AA-3715', 'TCGA-AA-A01X', 'TCGA-F4-6570', 'TCGA-EI-6511', 'TCGA-CM-5861', 'TCGA-CA-5254', 'TCGA-G4-6317', 'TCGA-DM-A28M', 'TCGA-EI-6506', 'TCGA-AG-A020', 'TCGA-AG-3583', 'TCGA-G4-6294', 'TCGA-CM-6165', 'TCGA-D5-6535', 'TCGA-D5-5541', 'TCGA-DM-A1DB', 'TCGA-AG-A01J', 'TCGA-AG-4001', 'TCGA-AG-A00Y', 'TCGA-AA-3949', 'TCGA-AA-3842', 'TCGA-CA-6716', 'TCGA-CM-5348', 'TCGA-AA-3696', 'TCGA-AA-3833', 'TCGA-NH-A6GC', 'TCGA-CK-4947', 'TCGA-AA-3846', 'TCGA-A6-2677', 'TCGA-A6-2684', 'TCGA-NH-A6GA', 'TCGA-F5-6811', 'TCGA-A6-4107', 'TCGA-F5-6571', 'TCGA-CK-6747', 'TCGA-AA-3666', 'TCGA-CK-5914', 'TCGA-G4-6627', 'TCGA-DC-6681', 'TCGA-AZ-4315', 'TCGA-DT-5265', 'TCGA-CM-4751', 'TCGA-CM-6168', 'TCGA-AY-5543', 'TCGA-AZ-6607', 'TCGA-AG-3901', 'TCGA-AA-3695', 'TCGA-AD-6963', 'TCGA-CK-5913', 'TCGA-AF-2692', 'TCGA-G4-6307', 'TCGA-AA-A01T', 'TCGA-DY-A1DG', 'TCGA-AA-A01C', 'TCGA-AZ-6603', 'TCGA-AG-3727', 'TCGA-AG-3909', 'TCGA-D5-6539', 'TCGA-AZ-5407', 'TCGA-NH-A6GB', 'TCGA-AA-3851', 'TCGA-AF-5654', 'TCGA-AA-3530', 'TCGA-F5-6813', 'TCGA-AA-3845', 'TCGA-AG-3584', 'TCGA-AM-5820', 'TCGA-AA-3982', 'TCGA-D5-6924', 'TCGA-AA-3984', 'TCGA-5M-AATE', 'TCGA-AG-3894', 'TCGA-AU-3779', 'TCGA-A6-2671', 'TCGA-DC-6682', 'TCGA-A6-6653', 'TCGA-DM-A28E', 'TCGA-AZ-5403', 'TCGA-CK-4948', 'TCGA-AF-6655', 'TCGA-AA-A01P', 'TCGA-AA-A024', 'TCGA-F4-6809', 'TCGA-AA-3548', 'TCGA-QG-A5YW', 'TCGA-AA-3818', 'TCGA-EI-6513', 'TCGA-AY-4071', 'TCGA-D5-6532', 'TCGA-AZ-6600', 'TCGA-CM-6172', 'TCGA-AG-A01Y', 'TCGA-AA-3675', 'TCGA-AD-6888', 'TCGA-AH-6897', 'TCGA-AA-3852', 'TCGA-CM-4747', 'TCGA-AG-A02N', 'TCGA-AA-A02F', 'TCGA-DM-A1D7', 'TCGA-AA-3678', 'TCGA-CM-6677', 'TCGA-AA-A004', 'TCGA-D5-5539', 'TCGA-F5-6861', 'TCGA-AA-3544', 'TCGA-F4-6463', 'TCGA-AG-4015', 'TCGA-AF-3400', 'TCGA-CM-6169', 'TCGA-F4-6461', 'TCGA-AD-6901', 'TCGA-DM-A280', 'TCGA-AA-3819', 'TCGA-DM-A0X9', 'TCGA-G4-6315', 'TCGA-AA-3850', 'TCGA-CA-5797', 'TCGA-AA-3856', 'TCGA-AY-6386', 'TCGA-AG-3598', 'TCGA-CM-5863', 'TCGA-AG-3605', 'TCGA-AZ-6599', 'TCGA-CM-5344', 'TCGA-AA-3693', 'TCGA-G4-6306', 'TCGA-F4-6854', 'TCGA-A6-5656', 'TCGA-DY-A0XA', 'TCGA-A6-2685', 'TCGA-AY-6197', 'TCGA-4N-A93T', 'TCGA-NH-A50U', 'TCGA-A6-2683', 'TCGA-AA-3952', 'TCGA-DC-6160', 'TCGA-A6-3807', 'TCGA-AA-3522', 'TCGA-G4-6304', 'TCGA-AA-3526', 'TCGA-DC-4745', 'TCGA-AA-3877', 'TCGA-CM-6162', 'TCGA-AA-3692', 'TCGA-AA-3524', 'TCGA-AG-3881', 'TCGA-AA-3681', 'TCGA-CK-4951', 'TCGA-AA-3534', 'TCGA-A6-A56B', 'TCGA-DM-A1D4', 'TCGA-5M-AAT5', 'TCGA-AG-4022', 'TCGA-AA-3812', 'TCGA-D5-6529', 'TCGA-AA-3673', 'TCGA-EI-6917', 'TCGA-EI-6884', 'TCGA-AG-A011', 'TCGA-AY-A8YK', 'TCGA-EI-6881', 'TCGA-A6-2678', 'TCGA-AA-3947', 'TCGA-A6-5659', 'TCGA-AA-3994', 'TCGA-D5-6534', 'TCGA-AG-3574', 'TCGA-F5-6465', 'TCGA-CA-6719', 'TCGA-AA-3532', 'TCGA-G4-6322', 'TCGA-AF-A56K', 'TCGA-AA-3864', 'TCGA-AA-3685', 'TCGA-AA-3986'] + valid_patients=['TCGA-AG-4021', 'TCGA-AG-A02X', 'TCGA-AA-A02H', 'TCGA-F4-6807', 'TCGA-G4-6311', 'TCGA-AF-A56L', 'TCGA-CM-4748', 'TCGA-AZ-6605', 'TCGA-G4-6299', 'TCGA-AA-3667', 'TCGA-5M-AAT4', 'TCGA-5M-AAT6', 'TCGA-F4-6704', 'TCGA-AH-6544', 'TCGA-3L-AA1B', 'TCGA-CK-4950', 'TCGA-AA-3956', 'TCGA-G4-6323', 'TCGA-AA-3831', 'TCGA-AH-6903', 'TCGA-CA-5796', 'TCGA-AA-3684', 'TCGA-EI-7002', 'TCGA-F4-6460', 'TCGA-AA-3844', 'TCGA-AA-3848', 'TCGA-D5-6928', 'TCGA-A6-5665', 'TCGA-AA-A01S', 'TCGA-DM-A28H', 'TCGA-DC-6157', 'TCGA-DY-A1DD', 'TCGA-AA-3519', 'TCGA-AA-3855', 'TCGA-F4-6806', 'TCGA-AA-3520', 'TCGA-CM-6675', 'TCGA-AG-3898', 'TCGA-CK-6748', 'TCGA-G4-6298', 'TCGA-G4-6626', 'TCGA-QG-A5Z1', 'TCGA-DM-A28G', 'TCGA-A6-2681', 'TCGA-DM-A28F', 'TCGA-G4-6586', 'TCGA-CK-5916', 'TCGA-AG-A002', 'TCGA-AG-A026', 'TCGA-AA-A02E', 'TCGA-F5-6464', 'TCGA-EI-6883', 'TCGA-F4-6856', 'TCGA-AG-A015', 'TCGA-CM-5349', 'TCGA-DM-A1DA', 'TCGA-D5-6530', 'TCGA-A6-A566', 'TCGA-NH-A50V', 'TCGA-F5-6814', 'TCGA-CM-6166', 'TCGA-CI-6622', 'TCGA-AA-3977', 'TCGA-CA-6718', 'TCGA-AA-3841', 'TCGA-AA-3521', 'TCGA-NH-A50T', 'TCGA-AA-A01R', 'TCGA-AG-A008', 'TCGA-CM-4746', 'TCGA-AA-A02R', 'TCGA-DC-6154', 'TCGA-AA-3531', 'TCGA-F5-6863', 'TCGA-F4-6703', 'TCGA-AY-A54L', 'TCGA-AA-3811', 'TCGA-AA-3814', 'TCGA-CK-5912', 'TCGA-A6-3809', 'TCGA-A6-5657', 'TCGA-EF-5830', 'TCGA-CM-6164', 'TCGA-SS-A7HO', 'TCGA-CM-5864', 'TCGA-AF-4110', 'TCGA-AF-6672', 'TCGA-AA-3866', 'TCGA-AA-A01V', 'TCGA-AA-A00N', 'TCGA-D5-6538', 'TCGA-CM-6170', 'TCGA-D5-6537', 'TCGA-AA-A02Y', 'TCGA-DY-A1DC', 'TCGA-AG-3883', 'TCGA-AD-5900', 'TCGA-AA-3955', 'TCGA-AG-3581', 'TCGA-A6-5664', 'TCGA-DY-A1DF', 'TCGA-AA-A02O', 'TCGA-AA-3527', 'TCGA-F5-6702', 'TCGA-AH-6643', 'TCGA-G4-6293', 'TCGA-DC-5337', 'TCGA-AA-A03F', 'TCGA-CI-6624', 'TCGA-AG-3602', 'TCGA-AA-3538', 'TCGA-AA-A01Z', 'TCGA-AA-3558', 'TCGA-AA-3560', 'TCGA-AA-3867', 'TCGA-A6-6650', 'TCGA-CK-5915', 'TCGA-A6-6651', 'TCGA-AY-4070', 'TCGA-AF-A56N', 'TCGA-D5-6533', 'TCGA-AA-3529', 'TCGA-AG-A032', 'TCGA-QG-A5YX'] + + ''' + model = LitVisionTransformer(categories=['WT', 'MUT'], + dim_input=1536, + category_weights=tensor([0.8676, 0.1324]), + dim_model=512, + dim_feedforward=512, n_heads=8, n_layers=2, - dropout=0.25) - model.set( - categories=train_categories, - category_weights=category_weights, - dim_input=dim_feats, + dropout=0.25, + use_alibi=False, + ground_truth_label='isMSIH', + train_patients=['TCGA-AH-6644', 'TCGA-AA-3664', 'TCGA-AZ-4614', 'TCGA-D5-5537', + 'TCGA-G4-6628', 'TCGA-EI-6882', 'TCGA-AF-2689', 'TCGA-F4-6805', + 'TCGA-AZ-4681', 'TCGA-AG-3882', 'TCGA-A6-6648', 'TCGA-AA-A017', + 'TCGA-G4-6303', 'TCGA-EI-6508', 'TCGA-AF-6136', 'TCGA-A6-6654', + 'TCGA-AD-6895', 'TCGA-CM-5860', 'TCGA-A6-4105', 'TCGA-F5-6810', + 'TCGA-QG-A5YV', 'TCGA-DC-6155', 'TCGA-F5-6812', 'TCGA-QL-A97D', + 'TCGA-AZ-4615', 'TCGA-CK-6751', 'TCGA-AH-6547', 'TCGA-AA-3980', + 'TCGA-CL-5918', 'TCGA-DM-A282', 'TCGA-AA-3514', 'TCGA-EI-6509', + 'TCGA-AF-3911', 'TCGA-CL-5917', 'TCGA-A6-2686', 'TCGA-AA-3950', + 'TCGA-AA-3549', 'TCGA-AA-3973', 'TCGA-D5-6922', 'TCGA-AG-3887', + 'TCGA-CA-6717', 'TCGA-CM-6171', 'TCGA-AD-6964', 'TCGA-A6-6142', + 'TCGA-AD-6890', 'TCGA-A6-6138', 'TCGA-AD-A5EK', 'TCGA-D5-6929', + 'TCGA-G4-6588', 'TCGA-AZ-4616', 'TCGA-AA-3561', 'TCGA-QG-A5Z2', + 'TCGA-DM-A1HA', 'TCGA-CL-4957', 'TCGA-CA-5255', 'TCGA-DC-6158', + 'TCGA-G4-6295', 'TCGA-AY-A69D', 'TCGA-DM-A1D6', 'TCGA-AG-3896', + 'TCGA-A6-6652', 'TCGA-CM-6680', 'TCGA-AA-3821', 'TCGA-D5-5540', + 'TCGA-DC-5869', 'TCGA-AG-4008', 'TCGA-CK-4952', 'TCGA-AA-3875', + 'TCGA-AA-A02W', 'TCGA-EI-6514', 'TCGA-DM-A288', 'TCGA-AG-3890', + 'TCGA-AA-3979', 'TCGA-NH-A5IV', 'TCGA-G4-6297', 'TCGA-D5-6927', + 'TCGA-AA-3680', 'TCGA-NH-A8F8', 'TCGA-A6-5662', 'TCGA-A6-2674', + 'TCGA-AG-A01N', 'TCGA-DM-A1D8', 'TCGA-AA-3837', 'TCGA-AA-A01I', + 'TCGA-DM-A0XF', 'TCGA-F4-6855', 'TCGA-CA-6715', 'TCGA-AA-3975', + 'TCGA-AA-3989', 'TCGA-AG-A016', 'TCGA-A6-5666', 'TCGA-CK-6746', + 'TCGA-CM-4752', 'TCGA-CM-6676', 'TCGA-D5-6541', 'TCGA-A6-5667', + 'TCGA-AF-2691', 'TCGA-AD-A5EJ', 'TCGA-F5-6864', 'TCGA-AG-3726', + 'TCGA-A6-6649', 'TCGA-AA-A03J', 'TCGA-AF-2687', 'TCGA-AA-3858', + 'TCGA-G4-6321', 'TCGA-G5-6641', 'TCGA-G4-6309', 'TCGA-D5-5538', + 'TCGA-DM-A28K', 'TCGA-AG-3878', 'TCGA-AZ-6598', 'TCGA-AA-3688', + 'TCGA-AG-A01W', 'TCGA-AA-3517', 'TCGA-D5-6898', 'TCGA-CM-5341', + 'TCGA-CM-6167', 'TCGA-D5-6531', 'TCGA-DC-6683', 'TCGA-AF-2690', + 'TCGA-CM-6161', 'TCGA-A6-5660', 'TCGA-NH-A8F7', 'TCGA-CM-6163', + 'TCGA-AA-3968', 'TCGA-CM-5868', 'TCGA-AA-3976', 'TCGA-A6-A565', + 'TCGA-AM-5821', 'TCGA-AD-6965', 'TCGA-AY-A71X', 'TCGA-AZ-4682', + 'TCGA-AG-3892', 'TCGA-A6-2675', 'TCGA-F4-6569', 'TCGA-AG-3893', + 'TCGA-CM-5862', 'TCGA-AZ-6606', 'TCGA-CM-6678', 'TCGA-D5-6931', + 'TCGA-D5-6923', 'TCGA-AG-3575', 'TCGA-AA-A00Z', 'TCGA-D5-6540', + 'TCGA-A6-3808', 'TCGA-CM-4743', 'TCGA-DC-4749', 'TCGA-T9-A92H', + 'TCGA-A6-3810', 'TCGA-AD-6889', 'TCGA-G4-6320', 'TCGA-AA-3971', + 'TCGA-AZ-4308', 'TCGA-D5-7000', 'TCGA-DM-A28A', 'TCGA-AG-A01L', + 'TCGA-EI-6512', 'TCGA-A6-5661', 'TCGA-AG-3599', 'TCGA-AG-3902', + 'TCGA-AD-6548', 'TCGA-AA-A022', 'TCGA-AG-3580', 'TCGA-A6-A567', + 'TCGA-AA-3679', 'TCGA-CA-5256', 'TCGA-F4-6808', 'TCGA-D5-6536', + 'TCGA-DM-A1D9', 'TCGA-AG-3885', 'TCGA-DM-A1D0', 'TCGA-CM-6674', + 'TCGA-EI-6507', 'TCGA-F4-6459', 'TCGA-A6-6137', 'TCGA-AD-6899', + 'TCGA-EI-6885', 'TCGA-WS-AB45', 'TCGA-D5-6930', 'TCGA-AU-6004', + 'TCGA-AY-6196', 'TCGA-AA-A010', 'TCGA-AG-A00C', 'TCGA-4T-AA8H', + 'TCGA-G4-6302', 'TCGA-AA-3966', 'TCGA-AF-2693', 'TCGA-D5-6926', + 'TCGA-DM-A0XD', 'TCGA-AA-3854', 'TCGA-D5-6932', 'TCGA-EI-7004', + 'TCGA-AG-3594', 'TCGA-EI-6510', 'TCGA-AA-3715', 'TCGA-AA-A01X', + 'TCGA-F4-6570', 'TCGA-EI-6511', 'TCGA-CM-5861', 'TCGA-CA-5254', + 'TCGA-G4-6317', 'TCGA-DM-A28M', 'TCGA-EI-6506', 'TCGA-AG-A020', + 'TCGA-AG-3583', 'TCGA-G4-6294', 'TCGA-CM-6165', 'TCGA-D5-6535', + 'TCGA-D5-5541', 'TCGA-DM-A1DB', 'TCGA-AG-A01J', 'TCGA-AG-4001', + 'TCGA-AG-A00Y', 'TCGA-AA-3949', 'TCGA-AA-3842', 'TCGA-CA-6716', + 'TCGA-CM-5348', 'TCGA-AA-3696', 'TCGA-AA-3833', 'TCGA-NH-A6GC', + 'TCGA-CK-4947', 'TCGA-AA-3846', 'TCGA-A6-2677', 'TCGA-A6-2684', + 'TCGA-NH-A6GA', 'TCGA-F5-6811', 'TCGA-A6-4107', 'TCGA-F5-6571', + 'TCGA-CK-6747', 'TCGA-AA-3666', 'TCGA-CK-5914', 'TCGA-G4-6627', + 'TCGA-DC-6681', 'TCGA-AZ-4315', 'TCGA-DT-5265', 'TCGA-CM-4751', + 'TCGA-CM-6168', 'TCGA-AY-5543', 'TCGA-AZ-6607', 'TCGA-AG-3901', + 'TCGA-AA-3695', 'TCGA-AD-6963', 'TCGA-CK-5913', 'TCGA-AF-2692', + 'TCGA-G4-6307', 'TCGA-AA-A01T', 'TCGA-DY-A1DG', 'TCGA-AA-A01C', + 'TCGA-AZ-6603', 'TCGA-AG-3727', 'TCGA-AG-3909', 'TCGA-D5-6539', + 'TCGA-AZ-5407', 'TCGA-NH-A6GB', 'TCGA-AA-3851', 'TCGA-AF-5654', + 'TCGA-AA-3530', 'TCGA-F5-6813', 'TCGA-AA-3845', 'TCGA-AG-3584', + 'TCGA-AM-5820', 'TCGA-AA-3982', 'TCGA-D5-6924', 'TCGA-AA-3984', + 'TCGA-5M-AATE', 'TCGA-AG-3894', 'TCGA-AU-3779', 'TCGA-A6-2671', + 'TCGA-DC-6682', 'TCGA-A6-6653', 'TCGA-DM-A28E', 'TCGA-AZ-5403', + 'TCGA-CK-4948', 'TCGA-AF-6655', 'TCGA-AA-A01P', 'TCGA-AA-A024', + 'TCGA-F4-6809', 'TCGA-AA-3548', 'TCGA-QG-A5YW', 'TCGA-AA-3818', + 'TCGA-EI-6513', 'TCGA-AY-4071', 'TCGA-D5-6532', 'TCGA-AZ-6600', + 'TCGA-CM-6172', 'TCGA-AG-A01Y', 'TCGA-AA-3675', 'TCGA-AD-6888', + 'TCGA-AH-6897', 'TCGA-AA-3852', 'TCGA-CM-4747', 'TCGA-AG-A02N', + 'TCGA-AA-A02F', 'TCGA-DM-A1D7', 'TCGA-AA-3678', 'TCGA-CM-6677', + 'TCGA-AA-A004', 'TCGA-D5-5539', 'TCGA-F5-6861', 'TCGA-AA-3544', + 'TCGA-F4-6463', 'TCGA-AG-4015', 'TCGA-AF-3400', 'TCGA-CM-6169', + 'TCGA-F4-6461', 'TCGA-AD-6901', 'TCGA-DM-A280', 'TCGA-AA-3819', + 'TCGA-DM-A0X9', 'TCGA-G4-6315', 'TCGA-AA-3850', 'TCGA-CA-5797', + 'TCGA-AA-3856', 'TCGA-AY-6386', 'TCGA-AG-3598', 'TCGA-CM-5863', + 'TCGA-AG-3605', 'TCGA-AZ-6599', 'TCGA-CM-5344', 'TCGA-AA-3693', + 'TCGA-G4-6306', 'TCGA-F4-6854', 'TCGA-A6-5656', 'TCGA-DY-A0XA', + 'TCGA-A6-2685', 'TCGA-AY-6197', 'TCGA-4N-A93T', 'TCGA-NH-A50U', + 'TCGA-A6-2683', 'TCGA-AA-3952', 'TCGA-DC-6160', 'TCGA-A6-3807', + 'TCGA-AA-3522', 'TCGA-G4-6304', 'TCGA-AA-3526', 'TCGA-DC-4745', + 'TCGA-AA-3877', 'TCGA-CM-6162', 'TCGA-AA-3692', 'TCGA-AA-3524', + 'TCGA-AG-3881', 'TCGA-AA-3681', 'TCGA-CK-4951', 'TCGA-AA-3534', + 'TCGA-A6-A56B', 'TCGA-DM-A1D4', 'TCGA-5M-AAT5', 'TCGA-AG-4022', + 'TCGA-AA-3812', 'TCGA-D5-6529', 'TCGA-AA-3673', 'TCGA-EI-6917', + 'TCGA-EI-6884', 'TCGA-AG-A011', 'TCGA-AY-A8YK', 'TCGA-EI-6881', + 'TCGA-A6-2678', 'TCGA-AA-3947', 'TCGA-A6-5659', 'TCGA-AA-3994', + 'TCGA-D5-6534', 'TCGA-AG-3574', 'TCGA-F5-6465', 'TCGA-CA-6719', + 'TCGA-AA-3532', 'TCGA-G4-6322', 'TCGA-AF-A56K', 'TCGA-AA-3864', + 'TCGA-AA-3685', 'TCGA-AA-3986'], + valid_patients=['TCGA-AG-4021', 'TCGA-AG-A02X', 'TCGA-AA-A02H', 'TCGA-F4-6807', + 'TCGA-G4-6311', 'TCGA-AF-A56L', 'TCGA-CM-4748', 'TCGA-AZ-6605', + 'TCGA-G4-6299', 'TCGA-AA-3667', 'TCGA-5M-AAT4', 'TCGA-5M-AAT6', + 'TCGA-F4-6704', 'TCGA-AH-6544', 'TCGA-3L-AA1B', 'TCGA-CK-4950', + 'TCGA-AA-3956', 'TCGA-G4-6323', 'TCGA-AA-3831', 'TCGA-AH-6903', + 'TCGA-CA-5796', 'TCGA-AA-3684', 'TCGA-EI-7002', 'TCGA-F4-6460', + 'TCGA-AA-3844', 'TCGA-AA-3848', 'TCGA-D5-6928', 'TCGA-A6-5665', + 'TCGA-AA-A01S', 'TCGA-DM-A28H', 'TCGA-DC-6157', 'TCGA-DY-A1DD', + 'TCGA-AA-3519', 'TCGA-AA-3855', 'TCGA-F4-6806', 'TCGA-AA-3520', + 'TCGA-CM-6675', 'TCGA-AG-3898', 'TCGA-CK-6748', 'TCGA-G4-6298', + 'TCGA-G4-6626', 'TCGA-QG-A5Z1', 'TCGA-DM-A28G', 'TCGA-A6-2681', + 'TCGA-DM-A28F', 'TCGA-G4-6586', 'TCGA-CK-5916', 'TCGA-AG-A002', + 'TCGA-AG-A026', 'TCGA-AA-A02E', 'TCGA-F5-6464', 'TCGA-EI-6883', + 'TCGA-F4-6856', 'TCGA-AG-A015', 'TCGA-CM-5349', 'TCGA-DM-A1DA', + 'TCGA-D5-6530', 'TCGA-A6-A566', 'TCGA-NH-A50V', 'TCGA-F5-6814', + 'TCGA-CM-6166', 'TCGA-CI-6622', 'TCGA-AA-3977', 'TCGA-CA-6718', + 'TCGA-AA-3841', 'TCGA-AA-3521', 'TCGA-NH-A50T', 'TCGA-AA-A01R', + 'TCGA-AG-A008', 'TCGA-CM-4746', 'TCGA-AA-A02R', 'TCGA-DC-6154', + 'TCGA-AA-3531', 'TCGA-F5-6863', 'TCGA-F4-6703', 'TCGA-AY-A54L', + 'TCGA-AA-3811', 'TCGA-AA-3814', 'TCGA-CK-5912', 'TCGA-A6-3809', + 'TCGA-A6-5657', 'TCGA-EF-5830', 'TCGA-CM-6164', 'TCGA-SS-A7HO', + 'TCGA-CM-5864', 'TCGA-AF-4110', 'TCGA-AF-6672', 'TCGA-AA-3866', + 'TCGA-AA-A01V', 'TCGA-AA-A00N', 'TCGA-D5-6538', 'TCGA-CM-6170', + 'TCGA-D5-6537', 'TCGA-AA-A02Y', 'TCGA-DY-A1DC', 'TCGA-AG-3883', + 'TCGA-AD-5900', 'TCGA-AA-3955', 'TCGA-AG-3581', 'TCGA-A6-5664', + 'TCGA-DY-A1DF', 'TCGA-AA-A02O', 'TCGA-AA-3527', 'TCGA-F5-6702', + 'TCGA-AH-6643', 'TCGA-G4-6293', 'TCGA-DC-5337', 'TCGA-AA-A03F', + 'TCGA-CI-6624', 'TCGA-AG-3602', 'TCGA-AA-3538', 'TCGA-AA-A01Z', + 'TCGA-AA-3558', 'TCGA-AA-3560', 'TCGA-AA-3867', 'TCGA-A6-6650', + 'TCGA-CK-5915', 'TCGA-A6-6651', 'TCGA-AY-4070', 'TCGA-AF-A56N', + 'TCGA-D5-6533', 'TCGA-AA-3529', 'TCGA-AG-A032', 'TCGA-QG-A5YX'], + ) + + ''' + model = ModelClass( + categories=[np.str_('MUT'), np.str_('WT')], + dim_input = 1536, + category_weights = tensor([0.8676, 0.1324]), + dim_model=512, + dim_feedforward=512, + n_heads=8, + n_layers=2, + dropout=0.25, + use_alibi=use_alibi, ground_truth_label=ground_truth_label, train_patients=train_patients, valid_patients=valid_patients, clini_table=clini_table, slide_table=slide_table, - feature_dir=feature_dir, + feature_dir=feature_dir,) + ''' + logger.info( + f"Model 'instantiated with {len(model.state_dict())} parameters." ) - return model, train_dl, valid_dl @@ -386,7 +556,7 @@ def main(): ) ''' output_dir = "/mnt/swarm_alpha/ECDP2025/pathology_data/TCGA/TCGA-CRC/STAMP_crossval_new" - logger.info('output_dir:', output_dir) + #logger.info('output_dir:', output_dir) # _add_file_handle_(_logger, output_dir=Path(output_dir)) #_logger.info("Using training configuration from environment variables.") diff --git a/application/jobs/stamp/app/custom/modeling/alibi.py b/application/jobs/stamp/app/custom/modeling/alibi.py new file mode 100644 index 00000000..69c61aed --- /dev/null +++ b/application/jobs/stamp/app/custom/modeling/alibi.py @@ -0,0 +1,147 @@ +import torch +from jaxtyping import Bool, Float +from torch import Tensor, nn + + +class _RunningMeanScaler(nn.Module): + """Scales values by the inverse of the mean of values seen before.""" + + def __init__(self, dtype=torch.float32) -> None: + super().__init__() + self.running_mean = nn.Buffer(torch.ones(1, dtype=dtype)) + self.items_so_far = nn.Buffer(torch.ones(1, dtype=dtype)) + + def forward(self, x: torch.Tensor) -> torch.Tensor: + if self.training: + # Welford's algorithm + self.running_mean.copy_( + (self.running_mean + (x - self.running_mean) / self.items_so_far).mean() + ) + self.items_so_far += 1 + + return x / self.running_mean + + +class _ALiBi(nn.Module): + # See MultiHeadAliBi + def __init__(self) -> None: + super().__init__() + + self.scale_distance = _RunningMeanScaler() + self.bias_scale = nn.Parameter(torch.rand(1)) + + def forward( + self, + *, + q: Float[Tensor, "batch query qk_feature"], + k: Float[Tensor, "batch key qk_feature"], + v: Float[Tensor, "batch key v_feature"], + coords_q: Float[Tensor, "batch query coord"], + coords_k: Float[Tensor, "batch key coord"], + attn_mask: Bool[Tensor, "batch query key"] | None, + alibi_mask: Bool[Tensor, "batch query key"] | None, + ) -> Float[Tensor, "batch query v_feature"]: + """ + Args: + alibi_mask: + Which query-key pairs to mask from ALiBi (i.e. don't apply ALiBi to). + """ + weight_logits = torch.einsum("bqf,bkf->bqk", q, k) * (k.size(-1) ** -0.5) + distances = torch.linalg.norm( + coords_q.unsqueeze(2) - coords_k.unsqueeze(1), dim=-1 + ) + scaled_distances = self.scale_distance(distances) * self.bias_scale + + if alibi_mask is not None: + scaled_distances = scaled_distances.where(~alibi_mask, 0.0) + + weights = torch.softmax(weight_logits, dim=-1) + + if attn_mask is not None: + weights = (weights - scaled_distances).where(~attn_mask, 0.0) + else: + weights = weights - scaled_distances + + attention = torch.einsum("bqk,bkf->bqf", weights, v) + + return attention + + +class MultiHeadALiBi(nn.Module): + """Attention with Linear Biases + + Based on + > PRESS, Ofir; SMITH, Noah A.; LEWIS, Mike. + > Train short, test long: Attention with linear biases enables input length extrapolation. + > arXiv preprint arXiv:2108.12409, 2021. + + Since the distances between in WSIs may be quite large, + we scale the distances by the mean distance seen during training. + """ + + def __init__( + self, + *, + embed_dim: int, + num_heads: int, + ) -> None: + super().__init__() + + if embed_dim % num_heads != 0: + raise ValueError(f"{embed_dim=} has to be divisible by {num_heads=}") + + self.query_encoders = nn.ModuleList( + [ + nn.Linear(in_features=embed_dim, out_features=embed_dim // num_heads) + for _ in range(num_heads) + ] + ) + self.key_encoders = nn.ModuleList( + [ + nn.Linear(in_features=embed_dim, out_features=embed_dim // num_heads) + for _ in range(num_heads) + ] + ) + self.value_encoders = nn.ModuleList( + [ + nn.Linear(in_features=embed_dim, out_features=embed_dim // num_heads) + for _ in range(num_heads) + ] + ) + + self.attentions = nn.ModuleList([_ALiBi() for _ in range(num_heads)]) + + self.fc = nn.Linear(in_features=embed_dim, out_features=embed_dim) + + def forward( + self, + *, + q: Float[Tensor, "batch query mh_qk_feature"], + k: Float[Tensor, "batch key mh_qk_feature"], + v: Float[Tensor, "batch key hm_v_feature"], + coords_q: Float[Tensor, "batch query coord"], + coords_k: Float[Tensor, "batch key coord"], + attn_mask: Bool[Tensor, "batch query key"] | None, + alibi_mask: Bool[Tensor, "batch query key"] | None, + ) -> Float[Tensor, "batch query mh_v_feature"]: + stacked_attentions = torch.stack( + [ + att( + q=q_enc(q), + k=k_enc(k), + v=v_enc(v), + coords_q=coords_q, + coords_k=coords_k, + attn_mask=attn_mask, + alibi_mask=alibi_mask, + ) + for q_enc, k_enc, v_enc, att in zip( + self.query_encoders, + self.key_encoders, + self.value_encoders, + self.attentions, + strict=True, + ) + ] + ) + return self.fc(stacked_attentions.permute(1, 2, 0, 3).flatten(-2, -1)) diff --git a/application/jobs/stamp/app/custom/modeling/lightning_model.py b/application/jobs/stamp/app/custom/modeling/lightning_model.py index 97089551..2dd5f64e 100644 --- a/application/jobs/stamp/app/custom/modeling/lightning_model.py +++ b/application/jobs/stamp/app/custom/modeling/lightning_model.py @@ -55,88 +55,63 @@ class LitVisionTransformer(lightning.LightningModule): **metadata: Additional metadata to store with the model. """ + supported_features = ["tile"] + def __init__( self, *, + categories: Sequence[Category], + category_weights: Float[Tensor, "category_weight"], # noqa: F821 + dim_input: int, dim_model: int, dim_feedforward: int, n_heads: int, n_layers: int, dropout: float, - **metadata, - ) -> None: - super().__init__() - - self.dim_model = dim_model - self.dim_feedforward = dim_feedforward - self.n_heads = n_heads - self.n_layers = n_layers - self.dropout = dropout - self.metadata = metadata - - # Parameters to be set later - self.categories = None - self.category_weights = None - self.ground_truth_label = None - self.train_patients = None - self.valid_patients = None - self.use_alibi = False - self.dim_input = None - - # Optional external metadata - self.clini_table = None - self.slide_table = None - self.feature_dir = None - - self.vision_transformer = None - self.valid_auroc = None - - def set( - self, - *, - categories: Sequence[Category], - category_weights: Float[Tensor, "category_weight"], - dim_input: int, + # Experimental features + # TODO remove default values for stamp 3; they're only here for backwards compatibility use_alibi: bool = False, + # Metadata used by other parts of stamp, but not by the model itself ground_truth_label: PandasLabel, train_patients: Iterable[PatientId], valid_patients: Iterable[PatientId], - clini_table=None, - slide_table=None, - feature_dir=None, - ): - if len(categories) != len(category_weights): - raise ValueError("the number of category weights must match the number of categories!") + # Other metadata + **metadata, + ) -> None: + super().__init__() - self.categories = np.array(categories) - self.category_weights = category_weights - self.dim_input = dim_input - self.use_alibi = use_alibi - self.ground_truth_label = ground_truth_label - self.train_patients = train_patients - self.valid_patients = valid_patients + if len(categories) != len(category_weights): + raise ValueError( + "the number of category weights has to match the number of categories!" + ) - self.clini_table = clini_table - self.slide_table = slide_table - self.feature_dir = feature_dir - # Initialize model and AUROC after all info is available self.vision_transformer = VisionTransformer( dim_output=len(categories), dim_input=dim_input, - dim_model=self.dim_model, - n_layers=self.n_layers, - n_heads=self.n_heads, - dim_feedforward=self.dim_feedforward, - dropout=self.dropout, + dim_model=dim_model, + n_layers=n_layers, + n_heads=n_heads, + dim_feedforward=dim_feedforward, + dropout=dropout, use_alibi=use_alibi, ) self.class_weights = category_weights self.valid_auroc = MulticlassAUROC(len(categories)) - self.save_hyperparameters(ignore=["vision_transformer", "valid_auroc"]) - def forward(self, bags: Bags) -> Float[Tensor, "batch logit"]: - if self.vision_transformer is None: - raise RuntimeError("Model not initialized. Call `.set()` first.") + # Used during deployment + self.ground_truth_label = ground_truth_label + self.categories = np.array(categories) + self.train_patients = train_patients + self.valid_patients = valid_patients + + _ = metadata # unused, but saved in model + + self.save_hyperparameters() + + def forward( + self, + bags: Bags, + ) -> Float[Tensor, "batch logit"]: return self.vision_transformer(bags) def _step( diff --git a/application/jobs/stamp/app/custom/modeling/mlp_classifier.py b/application/jobs/stamp/app/custom/modeling/mlp_classifier.py new file mode 100644 index 00000000..d99ec5e7 --- /dev/null +++ b/application/jobs/stamp/app/custom/modeling/mlp_classifier.py @@ -0,0 +1,123 @@ +from collections.abc import Iterable, Sequence + +import lightning +import numpy as np +import torch +from torch import Tensor, nn +from torchmetrics.classification import MulticlassAUROC + +from .types import Category, PandasLabel, PatientId + + +class MLPClassifier(nn.Module): + """ + Simple MLP for classification from a single feature vector. + """ + + def __init__( + self, + dim_input: int, + dim_hidden: int, + dim_output: int, + num_layers: int, + dropout: float, + ): + super().__init__() + layers = [] + in_dim = dim_input + for i in range(num_layers - 1): + layers.append(nn.Linear(in_dim, dim_hidden)) + layers.append(nn.ReLU()) + layers.append(nn.Dropout(dropout)) + in_dim = dim_hidden + layers.append(nn.Linear(in_dim, dim_output)) + self.mlp = nn.Sequential(*layers) + + def forward(self, x: Tensor) -> Tensor: + return self.mlp(x) + + +class LitMLPClassifier(lightning.LightningModule): + """ + PyTorch Lightning wrapper for MLPClassifier. + """ + + supported_features = ["patient"] + + def __init__( + self, + *, + categories: Sequence[Category], + category_weights: torch.Tensor, + dim_input: int, + dim_hidden: int, + num_layers: int, + dropout: float, + ground_truth_label: PandasLabel, + train_patients: Iterable[PatientId], + valid_patients: Iterable[PatientId], + **metadata, + ): + super().__init__() + self.save_hyperparameters() + self.model = MLPClassifier( + dim_input=dim_input, + dim_hidden=dim_hidden, + dim_output=len(categories), + num_layers=num_layers, + dropout=dropout, + ) + self.class_weights = category_weights + self.valid_auroc = MulticlassAUROC(len(categories)) + self.ground_truth_label = ground_truth_label + self.categories = np.array(categories) + self.train_patients = train_patients + self.valid_patients = valid_patients + + # TODO: Add version check with version 2.2.1, for both MLP and Transformer + + def forward(self, x: Tensor) -> Tensor: + return self.model(x) + + def _step(self, batch, step_name: str): + feats, targets = batch + logits = self.model(feats) + loss = nn.functional.cross_entropy( + logits, + targets.type_as(logits), + weight=self.class_weights.type_as(logits), + ) + self.log( + f"{step_name}_loss", + loss, + on_step=False, + on_epoch=True, + prog_bar=True, + sync_dist=True, + ) + if step_name == "validation": + self.valid_auroc.update(logits, targets.long().argmax(dim=-1)) + self.log( + f"{step_name}_auroc", + self.valid_auroc, + on_step=False, + on_epoch=True, + sync_dist=True, + ) + return loss + + def training_step(self, batch, batch_idx): + return self._step(batch, "training") + + def validation_step(self, batch, batch_idx): + return self._step(batch, "validation") + + def test_step(self, batch, batch_idx): + return self._step(batch, "test") + + def predict_step(self, batch, batch_idx): + feats, _ = batch + return self.model(feats) + + def configure_optimizers(self): + return torch.optim.Adam(self.parameters(), lr=1e-3) diff --git a/application/jobs/stamp/app/custom/modeling/module.py b/application/jobs/stamp/app/custom/modeling/module.py new file mode 100644 index 00000000..30fa7551 --- /dev/null +++ b/application/jobs/stamp/app/custom/modeling/module.py @@ -0,0 +1,1654 @@ +# Copyright The Lightning AI team. +# +# 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. +"""The LightningModule - an nn.Module with many additional features.""" + +import logging +import numbers +import weakref +from collections.abc import Generator, Mapping, Sequence +from contextlib import contextmanager +from io import BytesIO +from pathlib import Path +from typing import ( + IO, + TYPE_CHECKING, + Any, + Callable, + Literal, + Optional, + Union, + cast, + overload, +) + +import lightning.fabric as lf +import lightning.pytorch as pl +import torch +from lightning.fabric.loggers import Logger as FabricLogger +from lightning.fabric.utilities.apply_func import convert_to_tensors +from lightning.fabric.utilities.cloud_io import get_filesystem +from lightning.fabric.utilities.device_dtype_mixin import _DeviceDtypeModuleMixin +from lightning.fabric.utilities.types import _MAP_LOCATION_TYPE, _PATH +from lightning.fabric.wrappers import _FabricOptimizer +from lightning.pytorch.callbacks.callback import Callback +from lightning.pytorch.core.hooks import CheckpointHooks, DataHooks, ModelHooks +from lightning.pytorch.core.mixins import HyperparametersMixin +from lightning.pytorch.core.optimizer import LightningOptimizer +from lightning.pytorch.core.saving import _load_from_checkpoint +from lightning.pytorch.loggers import Logger +from lightning.pytorch.trainer import call +from lightning.pytorch.trainer.connectors.logger_connector.fx_validator import _FxValidator +from lightning.pytorch.trainer.connectors.logger_connector.result import _get_default_dtype +from lightning.pytorch.utilities import GradClipAlgorithmType +from lightning.pytorch.utilities.exceptions import MisconfigurationException +from lightning.pytorch.utilities.imports import _TORCHMETRICS_GREATER_EQUAL_0_9_1 +from lightning.pytorch.utilities.model_helpers import _restricted_classmethod +from lightning.pytorch.utilities.rank_zero import WarningCache, rank_zero_warn +from lightning.pytorch.utilities.signature_utils import is_param_in_hook_signature +from lightning.pytorch.utilities.types import ( + _METRIC, + STEP_OUTPUT, + LRSchedulerPLType, + LRSchedulerTypeUnion, + OptimizerLRScheduler, +) +from lightning_utilities.core.apply_func import apply_to_collection +from lightning_utilities.core.imports import RequirementCache +from torch import ScriptModule, Tensor +from torch.nn import Module +from torch.optim.optimizer import Optimizer +from torchmetrics import Metric, MetricCollection +from typing_extensions import Self, override + +if TYPE_CHECKING: + from torch.distributed.device_mesh import DeviceMesh + +_ONNX_AVAILABLE = RequirementCache("onnx") + +warning_cache = WarningCache() +log = logging.getLogger(__name__) + +MODULE_OPTIMIZERS = Union[ + Optimizer, LightningOptimizer, _FabricOptimizer, list[Optimizer], list[LightningOptimizer], list[_FabricOptimizer] +] + + +class LightningModule( + _DeviceDtypeModuleMixin, + HyperparametersMixin, + ModelHooks, + DataHooks, + CheckpointHooks, + Module, +): + # Below is for property support of JIT + # since none of these are important when using JIT, we are going to ignore them. + __jit_unused_properties__: list[str] = ( + [ + "example_input_array", + "on_gpu", + "current_epoch", + "global_step", + "global_rank", + "local_rank", + "logger", + "loggers", + "automatic_optimization", + "trainer", + "fabric", + "strict_loading", + "device_mesh", + ] + + _DeviceDtypeModuleMixin.__jit_unused_properties__ + + HyperparametersMixin.__jit_unused_properties__ + ) + _jit_is_scripting = False + + CHECKPOINT_HYPER_PARAMS_KEY = "hyper_parameters" + CHECKPOINT_HYPER_PARAMS_NAME = "hparams_name" + CHECKPOINT_HYPER_PARAMS_TYPE = "hparams_type" + + def __init__(self, *args: Any, **kwargs: Any) -> None: + super().__init__(*args, **kwargs) + + # pointer to the trainer object + self._trainer: Optional[pl.Trainer] = None + + # attributes that can be set by user + self._example_input_array: Optional[Union[Tensor, tuple, dict]] = None + self._automatic_optimization: bool = True + self._strict_loading: Optional[bool] = None + + # attributes used internally + self._current_fx_name: Optional[str] = None + self._param_requires_grad_state: dict[str, bool] = {} + self._metric_attributes: Optional[dict[int, str]] = None + self._compiler_ctx: Optional[dict[str, Any]] = None + + # attributes only used when using fabric + self._fabric: Optional[lf.Fabric] = None + self._fabric_optimizers: list[_FabricOptimizer] = [] + + # access to device mesh in `conigure_model()` hook + self._device_mesh: Optional[DeviceMesh] = None + + @overload + def optimizers( + self, use_pl_optimizer: Literal[True] = True + ) -> Union[LightningOptimizer, list[LightningOptimizer]]: + ... + + @overload + def optimizers(self, use_pl_optimizer: Literal[False]) -> Union[Optimizer, list[Optimizer]]: + ... + + @overload + def optimizers(self, use_pl_optimizer: bool) -> MODULE_OPTIMIZERS: + ... + + def optimizers(self, use_pl_optimizer: bool = True) -> MODULE_OPTIMIZERS: + """Returns the optimizer(s) that are being used during training. Useful for manual optimization. + + Args: + use_pl_optimizer: If ``True``, will wrap the optimizer(s) in a + :class:`~lightning.pytorch.core.optimizer.LightningOptimizer` for automatic handling of precision, + profiling, and counting of step calls for proper logging and checkpointing. It specifically wraps the + ``step`` method and custom optimizers that don't have this method are not supported. + + Returns: + A single optimizer, or a list of optimizers in case multiple ones are present. + + """ + if self._fabric: + opts: MODULE_OPTIMIZERS = self._fabric_optimizers + elif use_pl_optimizer: + opts = self.trainer.strategy._lightning_optimizers + else: + opts = self.trainer.optimizers + + # single optimizer + if ( + isinstance(opts, list) + and len(opts) == 1 + and isinstance(opts[0], (Optimizer, LightningOptimizer, _FabricOptimizer)) + ): + return opts[0] + # multiple opts + return opts + + def lr_schedulers(self) -> Union[None, list[LRSchedulerPLType], LRSchedulerPLType]: + """Returns the learning rate scheduler(s) that are being used during training. Useful for manual optimization. + + Returns: + A single scheduler, or a list of schedulers in case multiple ones are present, or ``None`` if no + schedulers were returned in :meth:`~lightning.pytorch.core.LightningModule.configure_optimizers`. + + """ + if not self.trainer.lr_scheduler_configs: + return None + + # ignore other keys "interval", "frequency", etc. + lr_schedulers: list[LRSchedulerPLType] = [config.scheduler for config in self.trainer.lr_scheduler_configs] + + # single scheduler + if len(lr_schedulers) == 1: + return lr_schedulers[0] + + # multiple schedulers + return lr_schedulers + + @property + def trainer(self) -> "pl.Trainer": + if self._fabric is not None: + return _TrainerFabricShim(fabric=self._fabric) # type: ignore[return-value] + if not self._jit_is_scripting and self._trainer is None: + raise RuntimeError(f"{self.__class__.__qualname__} is not attached to a `Trainer`.") + return self._trainer # type: ignore[return-value] + + @trainer.setter + def trainer(self, trainer: Optional["pl.Trainer"]) -> None: + for v in self.children(): + if isinstance(v, LightningModule): + v.trainer = trainer + self._trainer = trainer + + @property + def fabric(self) -> Optional["lf.Fabric"]: + return self._fabric + + @fabric.setter + def fabric(self, fabric: Optional["lf.Fabric"]) -> None: + for v in self.children(): + if isinstance(v, LightningModule): + v.fabric = fabric + if fabric is not None and not isinstance(fabric, weakref.ProxyTypes): + fabric = weakref.proxy(fabric) + self._fabric = fabric + + @property + def example_input_array(self) -> Optional[Union[Tensor, tuple, dict]]: + """The example input array is a specification of what the module can consume in the :meth:`forward` method. The + return type is interpreted as follows: + + - Single tensor: It is assumed the model takes a single argument, i.e., + ``model.forward(model.example_input_array)`` + - Tuple: The input array should be interpreted as a sequence of positional arguments, i.e., + ``model.forward(*model.example_input_array)`` + - Dict: The input array represents named keyword arguments, i.e., + ``model.forward(**model.example_input_array)`` + + """ + return self._example_input_array + + @example_input_array.setter + def example_input_array(self, example: Optional[Union[Tensor, tuple, dict]]) -> None: + self._example_input_array = example + + @property + def current_epoch(self) -> int: + """The current epoch in the ``Trainer``, or 0 if not attached.""" + return self.trainer.current_epoch if self._trainer else 0 + + @property + def global_step(self) -> int: + """Total training batches seen across all epochs. + + If no Trainer is attached, this property is 0. + + """ + return self.trainer.global_step if self._trainer else 0 + + @property + def global_rank(self) -> int: + """The index of the current process across all nodes and devices.""" + return self.trainer.global_rank if self._trainer else 0 + + @property + def local_rank(self) -> int: + """The index of the current process within a single node.""" + return self.trainer.local_rank if self._trainer else 0 + + @property + def on_gpu(self) -> bool: + """Returns ``True`` if this model is currently located on a GPU. + + Useful to set flags around the LightningModule for different CPU vs GPU behavior. + + """ + return self.device.type == "cuda" + + @property + def automatic_optimization(self) -> bool: + """If set to ``False`` you are responsible for calling ``.backward()``, ``.step()``, ``.zero_grad()``.""" + return self._automatic_optimization + + @automatic_optimization.setter + def automatic_optimization(self, automatic_optimization: bool) -> None: + self._automatic_optimization = automatic_optimization + + @property + def strict_loading(self) -> bool: + """Determines how Lightning loads this model using `.load_state_dict(..., strict=model.strict_loading)`.""" + # We use None as the default internally to determine whether the user has set a value + return self._strict_loading in (None, True) + + @strict_loading.setter + def strict_loading(self, strict_loading: bool) -> None: + self._strict_loading = strict_loading + + @property + def logger(self) -> Optional[Union[Logger, FabricLogger]]: + """Reference to the logger object in the Trainer.""" + if self._fabric is not None: + return self._fabric.logger + return self._trainer.logger if self._trainer is not None else None + + @property + def loggers(self) -> Union[list[Logger], list[FabricLogger]]: + """Reference to the list of loggers in the Trainer.""" + if self._fabric is not None: + return self._fabric.loggers + if self._trainer is not None: + return self._trainer.loggers + return [] + + @property + def device_mesh(self) -> Optional["DeviceMesh"]: + """Strategies like ``ModelParallelStrategy`` will create a device mesh that can be accessed in the + :meth:`~lightning.pytorch.core.hooks.ModelHooks.configure_model` hook to parallelize the LightningModule.""" + return self._device_mesh + + def _call_batch_hook(self, hook_name: str, *args: Any) -> Any: + trainer = self._trainer + if trainer: + datahook_selector = trainer._data_connector._datahook_selector + assert datahook_selector is not None + obj = datahook_selector.get_instance(hook_name) + if isinstance(obj, self.__class__): + trainer_method = call._call_lightning_module_hook + else: + trainer_method = call._call_lightning_datamodule_hook + + return trainer_method(trainer, hook_name, *args) + hook = getattr(self, hook_name) + return hook(*args) + + def _on_before_batch_transfer(self, batch: Any, dataloader_idx: int = 0) -> Any: + return self._call_batch_hook("on_before_batch_transfer", batch, dataloader_idx) + + def _apply_batch_transfer_handler( + self, batch: Any, device: Optional[torch.device] = None, dataloader_idx: int = 0 + ) -> Any: + device = device or self.device + batch = self._call_batch_hook("transfer_batch_to_device", batch, device, dataloader_idx) + batch = self._call_batch_hook("on_after_batch_transfer", batch, dataloader_idx) + return batch + + def print(self, *args: Any, **kwargs: Any) -> None: + r"""Prints only from process 0. Use this in any distributed mode to log only once. + + Args: + *args: The thing to print. The same as for Python's built-in print function. + **kwargs: The same as for Python's built-in print function. + + Example:: + + def forward(self, x): + self.print(x, 'in forward') + + """ + if self.trainer.is_global_zero: + progress_bar = self.trainer.progress_bar_callback + if progress_bar is not None and progress_bar.is_enabled: + progress_bar.print(*args, **kwargs) + else: + print(*args, **kwargs) + + def log( + self, + name: str, + value: _METRIC, + prog_bar: bool = False, + logger: Optional[bool] = None, + on_step: Optional[bool] = None, + on_epoch: Optional[bool] = None, + reduce_fx: Union[str, Callable] = "mean", + enable_graph: bool = False, + sync_dist: bool = False, + sync_dist_group: Optional[Any] = None, + add_dataloader_idx: bool = True, + batch_size: Optional[int] = None, + metric_attribute: Optional[str] = None, + rank_zero_only: bool = False, + ) -> None: + """Log a key, value pair. + + Example:: + + self.log('train_loss', loss) + + The default behavior per hook is documented here: :ref:`extensions/logging:Automatic Logging`. + + Args: + name: key to log. Must be identical across all processes if using DDP or any other distributed strategy. + value: value to log. Can be a ``float``, ``Tensor``, or a ``Metric``. + prog_bar: if ``True`` logs to the progress bar. + logger: if ``True`` logs to the logger. + on_step: if ``True`` logs at this step. The default value is determined by the hook. + See :ref:`extensions/logging:Automatic Logging` for details. + on_epoch: if ``True`` logs epoch accumulated metrics. The default value is determined by the hook. + See :ref:`extensions/logging:Automatic Logging` for details. + reduce_fx: reduction function over step values for end of epoch. :meth:`torch.mean` by default. + enable_graph: if ``True``, will not auto detach the graph. + sync_dist: if ``True``, reduces the metric across devices. Use with care as this may lead to a significant + communication overhead. + sync_dist_group: the DDP group to sync across. + add_dataloader_idx: if ``True``, appends the index of the current dataloader to + the name (when using multiple dataloaders). If False, user needs to give unique names for + each dataloader to not mix the values. + batch_size: Current batch_size. This will be directly inferred from the loaded batch, + but for some data structures you might need to explicitly provide it. + metric_attribute: To restore the metric state, Lightning requires the reference of the + :class:`torchmetrics.Metric` in your model. This is found automatically if it is a model attribute. + rank_zero_only: Tells Lightning if you are calling ``self.log`` from every process (default) or only from + rank 0. If ``True``, you won't be able to use this metric as a monitor in callbacks + (e.g., early stopping). Warning: Improper use can lead to deadlocks! See + :ref:`Advanced Logging ` for more details. + + """ + if self._fabric is not None: + self._log_dict_through_fabric(dictionary={name: value}, logger=logger) + return + + # check for invalid values + apply_to_collection(value, dict, self.__check_not_nested, name) + apply_to_collection( + value, object, self.__check_allowed, name, value, wrong_dtype=(numbers.Number, Metric, Tensor) + ) + + trainer = self._trainer + if trainer is None: + # not an error to support testing the `*_step` methods without a `Trainer` reference + rank_zero_warn( + "You are trying to `self.log()` but the `self.trainer` reference is not registered on the model yet." + " This is most likely because the model hasn't been passed to the `Trainer`" + ) + return + if trainer.barebones: + rank_zero_warn( + "You are trying to `self.log()` but `Trainer(barebones=True)` is configured." + " Logging can impact raw speed so it is disabled under this setting." + ) + return + results = trainer._results + if results is None: + raise MisconfigurationException( + "You are trying to `self.log()` but the loop's result collection is not registered" + " yet. This is most likely because you are trying to log in a `predict` hook," + " but it doesn't support logging" + ) + if self._current_fx_name is None: + raise MisconfigurationException( + "You are trying to `self.log()` but it is not managed by the `Trainer` control flow" + ) + + on_step, on_epoch = _FxValidator.check_logging_and_get_default_levels( + self._current_fx_name, on_step=on_step, on_epoch=on_epoch + ) + + # make sure user doesn't introduce logic for multi-dataloaders + if "/dataloader_idx_" in name: + raise MisconfigurationException( + f"You called `self.log` with the key `{name}`" + " but it should not contain information about `dataloader_idx`" + ) + + value = apply_to_collection(value, (Tensor, numbers.Number), self.__to_tensor, name) + + if trainer._logger_connector.should_reset_tensors(self._current_fx_name): + # if we started a new epoch (running its first batch) the hook name has changed + # reset any tensors for the new hook name + results.reset(metrics=False, fx=self._current_fx_name) + + if metric_attribute is None and isinstance(value, Metric): + if self._metric_attributes is None: + # compute once + self._metric_attributes = { + id(module): name for name, module in self.named_modules() if isinstance(module, Metric) + } + if not self._metric_attributes: + raise MisconfigurationException( + "Could not find the `LightningModule` attribute for the `torchmetrics.Metric` logged." + " You can fix this by setting an attribute for the metric in your `LightningModule`." + ) + # try to find the passed metric in the LightningModule + metric_attribute = self._metric_attributes.get(id(value), None) + if metric_attribute is None: + raise MisconfigurationException( + "Could not find the `LightningModule` attribute for the `torchmetrics.Metric` logged." + f" You can fix this by calling `self.log({name}, ..., metric_attribute=name)` where `name` is one" + f" of {list(self._metric_attributes.values())}" + ) + + if ( + trainer.training + and is_param_in_hook_signature(self.training_step, "dataloader_iter", explicit=True) + and batch_size is None + ): + raise MisconfigurationException( + "With `def training_step(self, dataloader_iter)`, `self.log(..., batch_size=...)` should be provided." + ) + + if logger and trainer.logger is None: + rank_zero_warn( + f"You called `self.log({name!r}, ..., logger=True)` but have no logger configured. You can enable one" + " by doing `Trainer(logger=ALogger(...))`" + ) + if logger is None: + # we could set false here if there's no configured logger, however, we still need to compute the "logged" + # metrics anyway because that's what the evaluation loops use as return value + logger = True + + results.log( + self._current_fx_name, + name, + value, + prog_bar=prog_bar, + logger=logger, + on_step=on_step, + on_epoch=on_epoch, + reduce_fx=reduce_fx, + enable_graph=enable_graph, + add_dataloader_idx=add_dataloader_idx, + batch_size=batch_size, + sync_dist=sync_dist and trainer._accelerator_connector.is_distributed, + sync_dist_fn=trainer.strategy.reduce, + sync_dist_group=sync_dist_group, + metric_attribute=metric_attribute, + rank_zero_only=rank_zero_only, + ) + + trainer._logger_connector._current_fx = self._current_fx_name + + def log_dict( + self, + dictionary: Union[Mapping[str, _METRIC], MetricCollection], + prog_bar: bool = False, + logger: Optional[bool] = None, + on_step: Optional[bool] = None, + on_epoch: Optional[bool] = None, + reduce_fx: Union[str, Callable] = "mean", + enable_graph: bool = False, + sync_dist: bool = False, + sync_dist_group: Optional[Any] = None, + add_dataloader_idx: bool = True, + batch_size: Optional[int] = None, + rank_zero_only: bool = False, + ) -> None: + """Log a dictionary of values at once. + + Example:: + + values = {'loss': loss, 'acc': acc, ..., 'metric_n': metric_n} + self.log_dict(values) + + Args: + dictionary: key value pairs. + Keys must be identical across all processes if using DDP or any other distributed strategy. + The values can be a ``float``, ``Tensor``, ``Metric``, or ``MetricCollection``. + prog_bar: if ``True`` logs to the progress base. + logger: if ``True`` logs to the logger. + on_step: if ``True`` logs at this step. + ``None`` auto-logs for training_step but not validation/test_step. + The default value is determined by the hook. + See :ref:`extensions/logging:Automatic Logging` for details. + on_epoch: if ``True`` logs epoch accumulated metrics. + ``None`` auto-logs for val/test step but not ``training_step``. + The default value is determined by the hook. + See :ref:`extensions/logging:Automatic Logging` for details. + reduce_fx: reduction function over step values for end of epoch. :meth:`torch.mean` by default. + enable_graph: if ``True``, will not auto-detach the graph + sync_dist: if ``True``, reduces the metric across GPUs/TPUs. Use with care as this may lead to a significant + communication overhead. + sync_dist_group: the ddp group to sync across. + add_dataloader_idx: if ``True``, appends the index of the current dataloader to + the name (when using multiple). If ``False``, user needs to give unique names for + each dataloader to not mix values. + batch_size: Current batch size. This will be directly inferred from the loaded batch, + but some data structures might need to explicitly provide it. + rank_zero_only: Tells Lightning if you are calling ``self.log`` from every process (default) or only from + rank 0. If ``True``, you won't be able to use this metric as a monitor in callbacks + (e.g., early stopping). Warning: Improper use can lead to deadlocks! See + :ref:`Advanced Logging ` for more details. + + """ + if self._fabric is not None: + return self._log_dict_through_fabric(dictionary=dictionary, logger=logger) + + kwargs: dict[str, bool] = {} + + if isinstance(dictionary, MetricCollection): + kwargs["keep_base"] = False + if _TORCHMETRICS_GREATER_EQUAL_0_9_1 and dictionary._enable_compute_groups: + kwargs["copy_state"] = False + + for k, v in dictionary.items(**kwargs): + self.log( + name=k, + value=v, + prog_bar=prog_bar, + logger=logger, + on_step=on_step, + on_epoch=on_epoch, + reduce_fx=reduce_fx, + enable_graph=enable_graph, + sync_dist=sync_dist, + sync_dist_group=sync_dist_group, + add_dataloader_idx=add_dataloader_idx, + batch_size=batch_size, + rank_zero_only=rank_zero_only, + ) + return None + + def _log_dict_through_fabric( + self, dictionary: Union[Mapping[str, _METRIC], MetricCollection], logger: Optional[bool] = None + ) -> None: + if logger is False: + # Passing `logger=False` with Fabric does not make much sense because there is no other destination to + # log to, but we support it in case the original code was written for Trainer use + return + + if any(isinstance(v, dict) for v in dictionary.values()): + raise ValueError(f"`self.log_dict({dictionary})` was called, but nested dictionaries cannot be logged") + for name, value in dictionary.items(): + apply_to_collection(value, object, self.__check_allowed, name, value, wrong_dtype=(numbers.Number, Tensor)) + + assert self._fabric is not None + self._fabric.log_dict(metrics=dictionary) # type: ignore[arg-type] + + @staticmethod + def __check_not_nested(value: dict, name: str) -> None: + # self-imposed restriction. for simplicity + if any(isinstance(v, dict) for v in value.values()): + raise ValueError(f"`self.log({name}, {value})` was called, but nested dictionaries cannot be logged") + + @staticmethod + def __check_allowed(v: Any, name: str, value: Any) -> None: + raise ValueError(f"`self.log({name}, {value})` was called, but `{type(v).__name__}` values cannot be logged") + + def __to_tensor(self, value: Union[Tensor, numbers.Number], name: str) -> Tensor: + value = ( + value.clone().detach() + if isinstance(value, Tensor) + else torch.tensor(value, device=self.device, dtype=_get_default_dtype()) + ) + if not torch.numel(value) == 1: + raise ValueError( + f"`self.log({name}, {value})` was called, but the tensor must have a single element." + f" You can try doing `self.log({name}, {value}.mean())`" + ) + value = value.squeeze() + return value + + def all_gather( + self, data: Union[Tensor, dict, list, tuple], group: Optional[Any] = None, sync_grads: bool = False + ) -> Union[Tensor, dict, list, tuple]: + r"""Gather tensors or collections of tensors from multiple processes. + + This method needs to be called on all processes and the tensors need to have the same shape across all + processes, otherwise your program will stall forever. + + Args: + data: int, float, tensor of shape (batch, ...), or a (possibly nested) collection thereof. + group: the process group to gather results from. Defaults to all processes (world) + sync_grads: flag that allows users to synchronize gradients for the all_gather operation + + Return: + A tensor of shape (world_size, batch, ...), or if the input was a collection + the output will also be a collection with tensors of this shape. For the special case where + world_size is 1, no additional dimension is added to the tensor(s). + + """ + group = group if group is not None else torch.distributed.group.WORLD + all_gather = self.trainer.strategy.all_gather + data = convert_to_tensors(data, device=self.device) + return apply_to_collection(data, Tensor, all_gather, group=group, sync_grads=sync_grads) + + @override + def forward(self, *args: Any, **kwargs: Any) -> Any: + r"""Same as :meth:`torch.nn.Module.forward`. + + Args: + *args: Whatever you decide to pass into the forward method. + **kwargs: Keyword arguments are also possible. + + Return: + Your model's output + + """ + return super().forward(*args, **kwargs) + + def training_step(self, *args: Any, **kwargs: Any) -> STEP_OUTPUT: + r"""Here you compute and return the training loss and some additional metrics for e.g. the progress bar or + logger. + + Args: + batch: The output of your data iterable, normally a :class:`~torch.utils.data.DataLoader`. + batch_idx: The index of this batch. + dataloader_idx: The index of the dataloader that produced this batch. + (only if multiple dataloaders used) + + Return: + - :class:`~torch.Tensor` - The loss tensor + - ``dict`` - A dictionary which can include any keys, but must include the key ``'loss'`` in the case of + automatic optimization. + - ``None`` - In automatic optimization, this will skip to the next batch (but is not supported for + multi-GPU, TPU, or DeepSpeed). For manual optimization, this has no special meaning, as returning + the loss is not required. + + In this step you'd normally do the forward pass and calculate the loss for a batch. + You can also do fancier things like multiple forward passes or something model specific. + + Example:: + + def training_step(self, batch, batch_idx): + x, y, z = batch + out = self.encoder(x) + loss = self.loss(out, x) + return loss + + To use multiple optimizers, you can switch to 'manual optimization' and control their stepping: + + .. code-block:: python + + def __init__(self): + super().__init__() + self.automatic_optimization = False + + + # Multiple optimizers (e.g.: GANs) + def training_step(self, batch, batch_idx): + opt1, opt2 = self.optimizers() + + # do training_step with encoder + ... + opt1.step() + # do training_step with decoder + ... + opt2.step() + + Note: + When ``accumulate_grad_batches`` > 1, the loss returned here will be automatically + normalized by ``accumulate_grad_batches`` internally. + + """ + rank_zero_warn("`training_step` must be implemented to be used with the Lightning Trainer") + + def validation_step(self, *args: Any, **kwargs: Any) -> STEP_OUTPUT: + r"""Operates on a single batch of data from the validation set. In this step you'd might generate examples or + calculate anything of interest like accuracy. + + Args: + batch: The output of your data iterable, normally a :class:`~torch.utils.data.DataLoader`. + batch_idx: The index of this batch. + dataloader_idx: The index of the dataloader that produced this batch. + (only if multiple dataloaders used) + + Return: + - :class:`~torch.Tensor` - The loss tensor + - ``dict`` - A dictionary. Can include any keys, but must include the key ``'loss'``. + - ``None`` - Skip to the next batch. + + .. code-block:: python + + # if you have one val dataloader: + def validation_step(self, batch, batch_idx): ... + + + # if you have multiple val dataloaders: + def validation_step(self, batch, batch_idx, dataloader_idx=0): ... + + Examples:: + + # CASE 1: A single validation dataset + def validation_step(self, batch, batch_idx): + x, y = batch + + # implement your own + out = self(x) + loss = self.loss(out, y) + + # log 6 example images + # or generated text... or whatever + sample_imgs = x[:6] + grid = torchvision.utils.make_grid(sample_imgs) + self.logger.experiment.add_image('example_images', grid, 0) + + # calculate acc + labels_hat = torch.argmax(out, dim=1) + val_acc = torch.sum(y == labels_hat).item() / (len(y) * 1.0) + + # log the outputs! + self.log_dict({'val_loss': loss, 'val_acc': val_acc}) + + If you pass in multiple val dataloaders, :meth:`validation_step` will have an additional argument. We recommend + setting the default value of 0 so that you can quickly switch between single and multiple dataloaders. + + .. code-block:: python + + # CASE 2: multiple validation dataloaders + def validation_step(self, batch, batch_idx, dataloader_idx=0): + # dataloader_idx tells you which dataset this is. + ... + + Note: + If you don't need to validate you don't need to implement this method. + + Note: + When the :meth:`validation_step` is called, the model has been put in eval mode + and PyTorch gradients have been disabled. At the end of validation, + the model goes back to training mode and gradients are enabled. + + """ + + def test_step(self, *args: Any, **kwargs: Any) -> STEP_OUTPUT: + r"""Operates on a single batch of data from the test set. In this step you'd normally generate examples or + calculate anything of interest such as accuracy. + + Args: + batch: The output of your data iterable, normally a :class:`~torch.utils.data.DataLoader`. + batch_idx: The index of this batch. + dataloader_idx: The index of the dataloader that produced this batch. + (only if multiple dataloaders used) + + Return: + - :class:`~torch.Tensor` - The loss tensor + - ``dict`` - A dictionary. Can include any keys, but must include the key ``'loss'``. + - ``None`` - Skip to the next batch. + + .. code-block:: python + + # if you have one test dataloader: + def test_step(self, batch, batch_idx): ... + + + # if you have multiple test dataloaders: + def test_step(self, batch, batch_idx, dataloader_idx=0): ... + + Examples:: + + # CASE 1: A single test dataset + def test_step(self, batch, batch_idx): + x, y = batch + + # implement your own + out = self(x) + loss = self.loss(out, y) + + # log 6 example images + # or generated text... or whatever + sample_imgs = x[:6] + grid = torchvision.utils.make_grid(sample_imgs) + self.logger.experiment.add_image('example_images', grid, 0) + + # calculate acc + labels_hat = torch.argmax(out, dim=1) + test_acc = torch.sum(y == labels_hat).item() / (len(y) * 1.0) + + # log the outputs! + self.log_dict({'test_loss': loss, 'test_acc': test_acc}) + + If you pass in multiple test dataloaders, :meth:`test_step` will have an additional argument. We recommend + setting the default value of 0 so that you can quickly switch between single and multiple dataloaders. + + .. code-block:: python + + # CASE 2: multiple test dataloaders + def test_step(self, batch, batch_idx, dataloader_idx=0): + # dataloader_idx tells you which dataset this is. + ... + + Note: + If you don't need to test you don't need to implement this method. + + Note: + When the :meth:`test_step` is called, the model has been put in eval mode and + PyTorch gradients have been disabled. At the end of the test epoch, the model goes back + to training mode and gradients are enabled. + + """ + + def predict_step(self, *args: Any, **kwargs: Any) -> Any: + """Step function called during :meth:`~lightning.pytorch.trainer.trainer.Trainer.predict`. By default, it calls + :meth:`~lightning.pytorch.core.LightningModule.forward`. Override to add any processing logic. + + The :meth:`~lightning.pytorch.core.LightningModule.predict_step` is used + to scale inference on multi-devices. + + To prevent an OOM error, it is possible to use :class:`~lightning.pytorch.callbacks.BasePredictionWriter` + callback to write the predictions to disk or database after each batch or on epoch end. + + The :class:`~lightning.pytorch.callbacks.BasePredictionWriter` should be used while using a spawn + based accelerator. This happens for ``Trainer(strategy="ddp_spawn")`` + or training on 8 TPU cores with ``Trainer(accelerator="tpu", devices=8)`` as predictions won't be returned. + + Args: + batch: The output of your data iterable, normally a :class:`~torch.utils.data.DataLoader`. + batch_idx: The index of this batch. + dataloader_idx: The index of the dataloader that produced this batch. + (only if multiple dataloaders used) + + Return: + Predicted output (optional). + + Example :: + + class MyModel(LightningModule): + + def predict_step(self, batch, batch_idx, dataloader_idx=0): + return self(batch) + + dm = ... + model = MyModel() + trainer = Trainer(accelerator="gpu", devices=2) + predictions = trainer.predict(model, dm) + + """ + # For backwards compatibility + batch = kwargs.get("batch", args[0]) + return self(batch) + + def configure_callbacks(self) -> Union[Sequence[Callback], Callback]: + """Configure model-specific callbacks. When the model gets attached, e.g., when ``.fit()`` or ``.test()`` gets + called, the list or a callback returned here will be merged with the list of callbacks passed to the Trainer's + ``callbacks`` argument. If a callback returned here has the same type as one or several callbacks already + present in the Trainer's callbacks list, it will take priority and replace them. In addition, Lightning will + make sure :class:`~lightning.pytorch.callbacks.model_checkpoint.ModelCheckpoint` callbacks run last. + + Return: + A callback or a list of callbacks which will extend the list of callbacks in the Trainer. + + Example:: + + def configure_callbacks(self): + early_stop = EarlyStopping(monitor="val_acc", mode="max") + checkpoint = ModelCheckpoint(monitor="val_loss") + return [early_stop, checkpoint] + + """ + return [] + + def configure_optimizers(self) -> OptimizerLRScheduler: + r"""Choose what optimizers and learning-rate schedulers to use in your optimization. Normally you'd need one. + But in the case of GANs or similar you might have multiple. Optimization with multiple optimizers only works in + the manual optimization mode. + + Return: + Any of these 6 options. + + - **Single optimizer**. + - **List or Tuple** of optimizers. + - **Two lists** - The first list has multiple optimizers, and the second has multiple LR schedulers + (or multiple ``lr_scheduler_config``). + - **Dictionary**, with an ``"optimizer"`` key, and (optionally) a ``"lr_scheduler"`` + key whose value is a single LR scheduler or ``lr_scheduler_config``. + - **None** - Fit will run without any optimizer. + + The ``lr_scheduler_config`` is a dictionary which contains the scheduler and its associated configuration. + The default configuration is shown below. + + .. code-block:: python + + lr_scheduler_config = { + # REQUIRED: The scheduler instance + "scheduler": lr_scheduler, + # The unit of the scheduler's step size, could also be 'step'. + # 'epoch' updates the scheduler on epoch end whereas 'step' + # updates it after a optimizer update. + "interval": "epoch", + # How many epochs/steps should pass between calls to + # `scheduler.step()`. 1 corresponds to updating the learning + # rate after every epoch/step. + "frequency": 1, + # Metric to monitor for schedulers like `ReduceLROnPlateau` + "monitor": "val_loss", + # If set to `True`, will enforce that the value specified 'monitor' + # is available when the scheduler is updated, thus stopping + # training if not found. If set to `False`, it will only produce a warning + "strict": True, + # If using the `LearningRateMonitor` callback to monitor the + # learning rate progress, this keyword can be used to specify + # a custom logged name + "name": None, + } + + When there are schedulers in which the ``.step()`` method is conditioned on a value, such as the + :class:`torch.optim.lr_scheduler.ReduceLROnPlateau` scheduler, Lightning requires that the + ``lr_scheduler_config`` contains the keyword ``"monitor"`` set to the metric name that the scheduler + should be conditioned on. + + .. testcode:: + + # The ReduceLROnPlateau scheduler requires a monitor + def configure_optimizers(self): + optimizer = Adam(...) + return { + "optimizer": optimizer, + "lr_scheduler": { + "scheduler": ReduceLROnPlateau(optimizer, ...), + "monitor": "metric_to_track", + "frequency": "indicates how often the metric is updated", + # If "monitor" references validation metrics, then "frequency" should be set to a + # multiple of "trainer.check_val_every_n_epoch". + }, + } + + + # In the case of two optimizers, only one using the ReduceLROnPlateau scheduler + def configure_optimizers(self): + optimizer1 = Adam(...) + optimizer2 = SGD(...) + scheduler1 = ReduceLROnPlateau(optimizer1, ...) + scheduler2 = LambdaLR(optimizer2, ...) + return ( + { + "optimizer": optimizer1, + "lr_scheduler": { + "scheduler": scheduler1, + "monitor": "metric_to_track", + }, + }, + {"optimizer": optimizer2, "lr_scheduler": scheduler2}, + ) + + Metrics can be made available to monitor by simply logging it using + ``self.log('metric_to_track', metric_val)`` in your :class:`~lightning.pytorch.core.LightningModule`. + + Note: + Some things to know: + + - Lightning calls ``.backward()`` and ``.step()`` automatically in case of automatic optimization. + - If a learning rate scheduler is specified in ``configure_optimizers()`` with key + ``"interval"`` (default "epoch") in the scheduler configuration, Lightning will call + the scheduler's ``.step()`` method automatically in case of automatic optimization. + - If you use 16-bit precision (``precision=16``), Lightning will automatically handle the optimizer. + - If you use :class:`torch.optim.LBFGS`, Lightning handles the closure function automatically for you. + - If you use multiple optimizers, you will have to switch to 'manual optimization' mode and step them + yourself. + - If you need to control how often the optimizer steps, override the :meth:`optimizer_step` hook. + + """ + rank_zero_warn("`configure_optimizers` must be implemented to be used with the Lightning Trainer") + + def manual_backward(self, loss: Tensor, *args: Any, **kwargs: Any) -> None: + """Call this directly from your :meth:`training_step` when doing optimizations manually. By using this, + Lightning can ensure that all the proper scaling gets applied when using mixed precision. + + See :ref:`manual optimization` for more examples. + + Example:: + + def training_step(...): + opt = self.optimizers() + loss = ... + opt.zero_grad() + # automatically applies scaling, etc... + self.manual_backward(loss) + opt.step() + + Args: + loss: The tensor on which to compute gradients. Must have a graph attached. + *args: Additional positional arguments to be forwarded to :meth:`~torch.Tensor.backward` + **kwargs: Additional keyword arguments to be forwarded to :meth:`~torch.Tensor.backward` + + """ + if self._fabric: + self._fabric.backward(loss, *args, **kwargs) + else: + self._verify_is_manual_optimization("manual_backward") + self.trainer.strategy.backward(loss, None, *args, **kwargs) + + def backward(self, loss: Tensor, *args: Any, **kwargs: Any) -> None: + """Called to perform backward on the loss returned in :meth:`training_step`. Override this hook with your own + implementation if you need to. + + Args: + loss: The loss tensor returned by :meth:`training_step`. If gradient accumulation is used, the loss here + holds the normalized value (scaled by 1 / accumulation steps). + + Example:: + + def backward(self, loss): + loss.backward() + + """ + if self._fabric: + self._fabric.backward(loss, *args, **kwargs) + else: + loss.backward(*args, **kwargs) + + def toggle_optimizer(self, optimizer: Union[Optimizer, LightningOptimizer]) -> None: + """Makes sure only the gradients of the current optimizer's parameters are calculated in the training step to + prevent dangling gradients in multiple-optimizer setup. + + It works with :meth:`untoggle_optimizer` to make sure ``param_requires_grad_state`` is properly reset. + + Args: + optimizer: The optimizer to toggle. + + """ + # Iterate over all optimizer parameters to preserve their `requires_grad` information + # in case these are pre-defined during `configure_optimizers` + param_requires_grad_state = {} + for opt in self.trainer.optimizers: + for group in opt.param_groups: + for param in group["params"]: + # If a param already appear in param_requires_grad_state, continue + if param in param_requires_grad_state: + continue + param_requires_grad_state[param] = param.requires_grad + param.requires_grad = False + + # Then iterate over the current optimizer's parameters and set its `requires_grad` + # properties accordingly + for group in optimizer.param_groups: + for param in group["params"]: + param.requires_grad = param_requires_grad_state[param] + self._param_requires_grad_state = param_requires_grad_state + + def untoggle_optimizer(self, optimizer: Union[Optimizer, LightningOptimizer]) -> None: + """Resets the state of required gradients that were toggled with :meth:`toggle_optimizer`. + + Args: + optimizer: The optimizer to untoggle. + + """ + for opt in self.trainer.optimizers: + if not (opt is optimizer or (isinstance(optimizer, LightningOptimizer) and opt is optimizer.optimizer)): + for group in opt.param_groups: + for param in group["params"]: + if param in self._param_requires_grad_state: + param.requires_grad = self._param_requires_grad_state[param] + # save memory + self._param_requires_grad_state = {} + + @contextmanager + def toggled_optimizer(self, optimizer: Union[Optimizer, LightningOptimizer]) -> Generator: + """Makes sure only the gradients of the current optimizer's parameters are calculated in the training step to + prevent dangling gradients in multiple-optimizer setup. Combines :meth:`toggle_optimizer` and + :meth:`untoggle_optimizer` into context manager. + + Args: + optimizer: The optimizer to toggle. + + Example:: + + def training_step(...): + opt = self.optimizers() + with self.toggled_optimizer(opt): + loss = ... + opt.zero_grad() + self.manual_backward(loss) + opt.step() + + """ + self.toggle_optimizer(optimizer) + try: + yield + finally: + self.untoggle_optimizer(optimizer) + + def clip_gradients( + self, + optimizer: Optimizer, + gradient_clip_val: Optional[Union[int, float]] = None, + gradient_clip_algorithm: Optional[str] = None, + ) -> None: + """Handles gradient clipping internally. + + Note: + - Do not override this method. If you want to customize gradient clipping, consider using + :meth:`configure_gradient_clipping` method. + - For manual optimization (``self.automatic_optimization = False``), if you want to use + gradient clipping, consider calling + ``self.clip_gradients(opt, gradient_clip_val=0.5, gradient_clip_algorithm="norm")`` + manually in the training step. + + Args: + optimizer: Current optimizer being used. + gradient_clip_val: The value at which to clip gradients. + gradient_clip_algorithm: The gradient clipping algorithm to use. Pass ``gradient_clip_algorithm="value"`` + to clip by value, and ``gradient_clip_algorithm="norm"`` to clip by norm. + + """ + + if self.fabric is not None: + self.fabric.clip_gradients( + self, + optimizer, + clip_val=gradient_clip_val if gradient_clip_algorithm == GradClipAlgorithmType.VALUE else None, + max_norm=None if gradient_clip_algorithm == GradClipAlgorithmType.VALUE else gradient_clip_val, + ) + return + + if gradient_clip_val is None: + gradient_clip_val = self.trainer.gradient_clip_val or 0.0 + elif self.trainer.gradient_clip_val is not None and self.trainer.gradient_clip_val != gradient_clip_val: + raise MisconfigurationException( + f"You have set `Trainer(gradient_clip_val={self.trainer.gradient_clip_val!r})`" + f" and have passed `clip_gradients(gradient_clip_val={gradient_clip_val!r})`." + " Please use only one of them." + ) + + if gradient_clip_algorithm is None: + gradient_clip_algorithm = self.trainer.gradient_clip_algorithm or "norm" + else: + gradient_clip_algorithm = gradient_clip_algorithm.lower() + if ( + self.trainer.gradient_clip_algorithm is not None + and self.trainer.gradient_clip_algorithm != gradient_clip_algorithm + ): + raise MisconfigurationException( + f"You have set `Trainer(gradient_clip_algorithm={self.trainer.gradient_clip_algorithm.value!r})`" + f" and have passed `clip_gradients(gradient_clip_algorithm={gradient_clip_algorithm!r})" + " Please use only one of them." + ) + + if not isinstance(gradient_clip_val, (int, float)): + raise TypeError(f"`gradient_clip_val` should be an int or a float. Got {gradient_clip_val}.") + + if not GradClipAlgorithmType.supported_type(gradient_clip_algorithm.lower()): + raise MisconfigurationException( + f"`gradient_clip_algorithm` {gradient_clip_algorithm} is invalid." + f" Allowed algorithms: {GradClipAlgorithmType.supported_types()}." + ) + + gradient_clip_algorithm = GradClipAlgorithmType(gradient_clip_algorithm) + self.trainer.precision_plugin.clip_gradients(optimizer, gradient_clip_val, gradient_clip_algorithm) + + def configure_gradient_clipping( + self, + optimizer: Optimizer, + gradient_clip_val: Optional[Union[int, float]] = None, + gradient_clip_algorithm: Optional[str] = None, + ) -> None: + """Perform gradient clipping for the optimizer parameters. Called before :meth:`optimizer_step`. + + Args: + optimizer: Current optimizer being used. + gradient_clip_val: The value at which to clip gradients. By default, value passed in Trainer + will be available here. + gradient_clip_algorithm: The gradient clipping algorithm to use. By default, value + passed in Trainer will be available here. + + Example:: + + def configure_gradient_clipping(self, optimizer, gradient_clip_val, gradient_clip_algorithm): + # Implement your own custom logic to clip gradients + # You can call `self.clip_gradients` with your settings: + self.clip_gradients( + optimizer, + gradient_clip_val=gradient_clip_val, + gradient_clip_algorithm=gradient_clip_algorithm + ) + + """ + self.clip_gradients( + optimizer, gradient_clip_val=gradient_clip_val, gradient_clip_algorithm=gradient_clip_algorithm + ) + + def lr_scheduler_step(self, scheduler: LRSchedulerTypeUnion, metric: Optional[Any]) -> None: + r"""Override this method to adjust the default way the :class:`~lightning.pytorch.trainer.trainer.Trainer` calls + each scheduler. By default, Lightning calls ``step()`` and as shown in the example for each scheduler based on + its ``interval``. + + Args: + scheduler: Learning rate scheduler. + metric: Value of the monitor used for schedulers like ``ReduceLROnPlateau``. + + Examples:: + + # DEFAULT + def lr_scheduler_step(self, scheduler, metric): + if metric is None: + scheduler.step() + else: + scheduler.step(metric) + + # Alternative way to update schedulers if it requires an epoch value + def lr_scheduler_step(self, scheduler, metric): + scheduler.step(epoch=self.current_epoch) + + """ + if metric is None: + scheduler.step() # type: ignore[call-arg] + else: + scheduler.step(metric) + + def optimizer_step( + self, + epoch: int, + batch_idx: int, + optimizer: Union[Optimizer, LightningOptimizer], + optimizer_closure: Optional[Callable[[], Any]] = None, + ) -> None: + r"""Override this method to adjust the default way the :class:`~lightning.pytorch.trainer.trainer.Trainer` calls + the optimizer. + + By default, Lightning calls ``step()`` and ``zero_grad()`` as shown in the example. + This method (and ``zero_grad()``) won't be called during the accumulation phase when + ``Trainer(accumulate_grad_batches != 1)``. Overriding this hook has no benefit with manual optimization. + + Args: + epoch: Current epoch + batch_idx: Index of current batch + optimizer: A PyTorch optimizer + optimizer_closure: The optimizer closure. This closure must be executed as it includes the + calls to ``training_step()``, ``optimizer.zero_grad()``, and ``backward()``. + + Examples:: + + def optimizer_step(self, epoch, batch_idx, optimizer, optimizer_closure): + # Add your custom logic to run directly before `optimizer.step()` + + optimizer.step(closure=optimizer_closure) + + # Add your custom logic to run directly after `optimizer.step()` + + """ + optimizer.step(closure=optimizer_closure) + + def optimizer_zero_grad(self, epoch: int, batch_idx: int, optimizer: Optimizer) -> None: + """Override this method to change the default behaviour of ``optimizer.zero_grad()``. + + Args: + epoch: Current epoch + batch_idx: Index of current batch + optimizer: A PyTorch optimizer + + Examples:: + + # DEFAULT + def optimizer_zero_grad(self, epoch, batch_idx, optimizer): + optimizer.zero_grad() + + # Set gradients to `None` instead of zero to improve performance (not required on `torch>=2.0.0`). + def optimizer_zero_grad(self, epoch, batch_idx, optimizer): + optimizer.zero_grad(set_to_none=True) + + See :meth:`torch.optim.Optimizer.zero_grad` for the explanation of the above example. + + """ + optimizer.zero_grad() + + def freeze(self) -> None: + r"""Freeze all params for inference. + + Example:: + + model = MyLightningModule(...) + model.freeze() + + """ + for param in self.parameters(): + param.requires_grad = False + + self.eval() + + def unfreeze(self) -> None: + """Unfreeze all parameters for training. + + .. code-block:: python + + model = MyLightningModule(...) + model.unfreeze() + + """ + for param in self.parameters(): + param.requires_grad = True + + self.train() + + def _verify_is_manual_optimization(self, fn_name: str) -> None: + if self.automatic_optimization: + raise MisconfigurationException( + f"to use {fn_name}, please disable automatic optimization:" + " set model property `automatic_optimization` as False" + ) + + @torch.no_grad() + def to_onnx(self, file_path: Union[str, Path, BytesIO], input_sample: Optional[Any] = None, **kwargs: Any) -> None: + """Saves the model in ONNX format. + + Args: + file_path: The path of the file the onnx model should be saved to. + input_sample: An input for tracing. Default: None (Use self.example_input_array) + **kwargs: Will be passed to torch.onnx.export function. + + Example:: + + class SimpleModel(LightningModule): + def __init__(self): + super().__init__() + self.l1 = torch.nn.Linear(in_features=64, out_features=4) + + def forward(self, x): + return torch.relu(self.l1(x.view(x.size(0), -1) + + model = SimpleModel() + input_sample = torch.randn(1, 64) + model.to_onnx("export.onnx", input_sample, export_params=True) + + """ + if not _ONNX_AVAILABLE: + raise ModuleNotFoundError(f"`{type(self).__name__}.to_onnx()` requires `onnx` to be installed.") + + mode = self.training + + if input_sample is None: + if self.example_input_array is None: + raise ValueError( + "Could not export to ONNX since neither `input_sample` nor" + " `model.example_input_array` attribute is set." + ) + input_sample = self.example_input_array + + input_sample = self._on_before_batch_transfer(input_sample) + input_sample = self._apply_batch_transfer_handler(input_sample) + + file_path = str(file_path) if isinstance(file_path, Path) else file_path + # PyTorch (2.5) declares file_path to be str | PathLike[Any] | None, but + # BytesIO does work, too. + torch.onnx.export(self, input_sample, file_path, **kwargs) # type: ignore + self.train(mode) + + @torch.no_grad() + def to_torchscript( + self, + file_path: Optional[Union[str, Path]] = None, + method: Optional[str] = "script", + example_inputs: Optional[Any] = None, + **kwargs: Any, + ) -> Union[ScriptModule, dict[str, ScriptModule]]: + """By default compiles the whole model to a :class:`~torch.jit.ScriptModule`. If you want to use tracing, + please provided the argument ``method='trace'`` and make sure that either the `example_inputs` argument is + provided, or the model has :attr:`example_input_array` set. If you would like to customize the modules that are + scripted you should override this method. In case you want to return multiple modules, we recommend using a + dictionary. + + Args: + file_path: Path where to save the torchscript. Default: None (no file saved). + method: Whether to use TorchScript's script or trace method. Default: 'script' + example_inputs: An input to be used to do tracing when method is set to 'trace'. + Default: None (uses :attr:`example_input_array`) + **kwargs: Additional arguments that will be passed to the :func:`torch.jit.script` or + :func:`torch.jit.trace` function. + + Note: + - Requires the implementation of the + :meth:`~lightning.pytorch.core.LightningModule.forward` method. + - The exported script will be set to evaluation mode. + - It is recommended that you install the latest supported version of PyTorch + to use this feature without limitations. See also the :mod:`torch.jit` + documentation for supported features. + + Example:: + + class SimpleModel(LightningModule): + def __init__(self): + super().__init__() + self.l1 = torch.nn.Linear(in_features=64, out_features=4) + + def forward(self, x): + return torch.relu(self.l1(x.view(x.size(0), -1))) + + model = SimpleModel() + model.to_torchscript(file_path="model.pt") + + torch.jit.save(model.to_torchscript( + file_path="model_trace.pt", method='trace', example_inputs=torch.randn(1, 64)) + ) + + Return: + This LightningModule as a torchscript, regardless of whether `file_path` is + defined or not. + + """ + mode = self.training + + if method == "script": + with _jit_is_scripting(): + torchscript_module = torch.jit.script(self.eval(), **kwargs) + elif method == "trace": + # if no example inputs are provided, try to see if model has example_input_array set + if example_inputs is None: + if self.example_input_array is None: + raise ValueError( + "Choosing method=`trace` requires either `example_inputs`" + " or `model.example_input_array` to be defined." + ) + example_inputs = self.example_input_array + + if kwargs.get("check_inputs") is not None: + kwargs["check_inputs"] = self._on_before_batch_transfer(kwargs["check_inputs"]) + kwargs["check_inputs"] = self._apply_batch_transfer_handler(kwargs["check_inputs"]) + + # automatically send example inputs to the right device and use trace + example_inputs = self._on_before_batch_transfer(example_inputs) + example_inputs = self._apply_batch_transfer_handler(example_inputs) + with _jit_is_scripting(): + torchscript_module = torch.jit.trace(func=self.eval(), example_inputs=example_inputs, **kwargs) + else: + raise ValueError(f"The 'method' parameter only supports 'script' or 'trace', but value given was: {method}") + + self.train(mode) + + if file_path is not None: + fs = get_filesystem(file_path) + with fs.open(file_path, "wb") as f: + torch.jit.save(torchscript_module, f) + + return torchscript_module + + @_restricted_classmethod + def load_from_checkpoint( + cls, + checkpoint_path: Union[_PATH, IO], + map_location: _MAP_LOCATION_TYPE = None, + hparams_file: Optional[_PATH] = None, + strict: Optional[bool] = None, + **kwargs: Any, + ) -> Self: + r"""Primary way of loading a model from a checkpoint. When Lightning saves a checkpoint it stores the arguments + passed to ``__init__`` in the checkpoint under ``"hyper_parameters"``. + + Any arguments specified through \*\*kwargs will override args stored in ``"hyper_parameters"``. + + Args: + checkpoint_path: Path to checkpoint. This can also be a URL, or file-like object + map_location: + If your checkpoint saved a GPU model and you now load on CPUs + or a different number of GPUs, use this to map to the new setup. + The behaviour is the same as in :func:`torch.load`. + hparams_file: Optional path to a ``.yaml`` or ``.csv`` file with hierarchical structure + as in this example:: + + drop_prob: 0.2 + dataloader: + batch_size: 32 + + You most likely won't need this since Lightning will always save the hyperparameters + to the checkpoint. + However, if your checkpoint weights don't have the hyperparameters saved, + use this method to pass in a ``.yaml`` file with the hparams you'd like to use. + These will be converted into a :class:`~dict` and passed into your + :class:`LightningModule` for use. + + If your model's ``hparams`` argument is :class:`~argparse.Namespace` + and ``.yaml`` file has hierarchical structure, you need to refactor your model to treat + ``hparams`` as :class:`~dict`. + strict: Whether to strictly enforce that the keys in :attr:`checkpoint_path` match the keys + returned by this module's state dict. Defaults to ``True`` unless ``LightningModule.strict_loading`` is + set, in which case it defaults to the value of ``LightningModule.strict_loading``. + \**kwargs: Any extra keyword args needed to init the model. Can also be used to override saved + hyperparameter values. + + Return: + :class:`LightningModule` instance with loaded weights and hyperparameters (if available). + + Note: + ``load_from_checkpoint`` is a **class** method. You should use your :class:`LightningModule` + **class** to call it instead of the :class:`LightningModule` instance, or a + ``TypeError`` will be raised. + + Note: + To ensure all layers can be loaded from the checkpoint, this function will call + :meth:`~lightning.pytorch.core.hooks.ModelHooks.configure_model` directly after instantiating the + model if this hook is overridden in your LightningModule. However, note that ``load_from_checkpoint`` does + not support loading sharded checkpoints, and you may run out of memory if the model is too large. In this + case, consider loading through the Trainer via ``.fit(ckpt_path=...)``. + + Example:: + + # load weights without mapping ... + model = MyLightningModule.load_from_checkpoint('path/to/checkpoint.ckpt') + + # or load weights mapping all weights from GPU 1 to GPU 0 ... + map_location = {'cuda:1':'cuda:0'} + model = MyLightningModule.load_from_checkpoint( + 'path/to/checkpoint.ckpt', + map_location=map_location + ) + + # or load weights and hyperparameters from separate files. + model = MyLightningModule.load_from_checkpoint( + 'path/to/checkpoint.ckpt', + hparams_file='/path/to/hparams_file.yaml' + ) + + # override some of the params with new values + model = MyLightningModule.load_from_checkpoint( + PATH, + num_layers=128, + pretrained_ckpt_path=NEW_PATH, + ) + + # predict + pretrained_model.eval() + pretrained_model.freeze() + y_hat = pretrained_model(x) + + """ + loaded = _load_from_checkpoint( + cls, + checkpoint_path, + map_location, + hparams_file, + strict, + **kwargs, + ) + return cast(Self, loaded) + + @override + def __getstate__(self) -> dict[str, Any]: + state = dict(self.__dict__) + state["_trainer"] = None + return state + + +@contextmanager +def _jit_is_scripting() -> Generator: + """Workaround for https://github.com/pytorch/pytorch/issues/67146.""" + LightningModule._jit_is_scripting = True + try: + yield + finally: + LightningModule._jit_is_scripting = False + + +class _TrainerFabricShim: + """Intercepts attribute access on LightningModule's trainer reference and redirects it to the Fabric object.""" + + def __init__(self, fabric: lf.Fabric) -> None: + super().__init__() + self._fabric = fabric + + def __getattr__(self, item: Any) -> Any: + try: + return getattr(self._fabric, item) + except AttributeError: + raise AttributeError( + f"Your LightningModule code tried to access `self.trainer.{item}` but this attribute is not available" + f" when using Fabric with a LightningModule." + ) diff --git a/application/jobs/stamp/app/custom/modeling/registry.py b/application/jobs/stamp/app/custom/modeling/registry.py new file mode 100644 index 00000000..e826cadd --- /dev/null +++ b/application/jobs/stamp/app/custom/modeling/registry.py @@ -0,0 +1,34 @@ +from enum import StrEnum +from typing import Sequence, Type, TypedDict + +import lightning + +from .lightning_model import LitVisionTransformer +from .mlp_classifier import LitMLPClassifier + + +class ModelName(StrEnum): + """Enum for available model names.""" + + VIT = "vit" + MLP = "mlp" + + +class ModelInfo(TypedDict): + """A dictionary to map a model to supported feature types. For example, + a linear classifier is not compatible with tile-evel feats.""" + + model_class: Type[lightning.LightningModule] + supported_features: Sequence[str] + + +MODEL_REGISTRY: dict[ModelName, ModelInfo] = { + ModelName.VIT: { + "model_class": LitVisionTransformer, + "supported_features": LitVisionTransformer.supported_features, + }, + ModelName.MLP: { + "model_class": LitMLPClassifier, + "supported_features": LitMLPClassifier.supported_features, + }, +} diff --git a/application/jobs/stamp/app/custom/modeling/vision_transformer.py b/application/jobs/stamp/app/custom/modeling/vision_transformer.py index 3a710960..3853c40b 100755 --- a/application/jobs/stamp/app/custom/modeling/vision_transformer.py +++ b/application/jobs/stamp/app/custom/modeling/vision_transformer.py @@ -6,12 +6,13 @@ from typing import assert_never, cast import torch -from alibi import MultiHeadALiBi from beartype import beartype from einops import repeat from jaxtyping import Bool, Float, jaxtyped from torch import Tensor, nn +from .alibi import MultiHeadALiBi + def feed_forward( dim: int, From c98001f18caa154d26885256d6f6683b6d4f086d Mon Sep 17 00:00:00 2001 From: GitHub CI Date: Wed, 30 Jul 2025 11:21:48 +0200 Subject: [PATCH 6/9] fix: update max_epochs parameter to 64 for improved training duration Signed-off-by: GitHub CI --- application/jobs/stamp/app/custom/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/jobs/stamp/app/custom/main.py b/application/jobs/stamp/app/custom/main.py index 18f73398..107cf0de 100644 --- a/application/jobs/stamp/app/custom/main.py +++ b/application/jobs/stamp/app/custom/main.py @@ -574,7 +574,7 @@ def main(): num_workers=15, # Training parameters batch_size=64, - max_epochs=1, # 64 + max_epochs=64, # 64 patience=16, accelerator="gpu", # Experimental features From 16eb24f7a97f7de96b5cee95f87773336eac8213 Mon Sep 17 00:00:00 2001 From: GitHub CI Date: Tue, 19 Aug 2025 10:29:51 +0200 Subject: [PATCH 7/9] delete unused files Signed-off-by: GitHub CI --- application/jobs/stamp/app/custom/alibi.py | 147 ------------ application/jobs/stamp/app/custom/crossval.py | 226 ------------------ application/jobs/stamp/app/custom/deploy.py | 218 ----------------- 3 files changed, 591 deletions(-) delete mode 100644 application/jobs/stamp/app/custom/alibi.py delete mode 100644 application/jobs/stamp/app/custom/crossval.py delete mode 100644 application/jobs/stamp/app/custom/deploy.py diff --git a/application/jobs/stamp/app/custom/alibi.py b/application/jobs/stamp/app/custom/alibi.py deleted file mode 100644 index 69c61aed..00000000 --- a/application/jobs/stamp/app/custom/alibi.py +++ /dev/null @@ -1,147 +0,0 @@ -import torch -from jaxtyping import Bool, Float -from torch import Tensor, nn - - -class _RunningMeanScaler(nn.Module): - """Scales values by the inverse of the mean of values seen before.""" - - def __init__(self, dtype=torch.float32) -> None: - super().__init__() - self.running_mean = nn.Buffer(torch.ones(1, dtype=dtype)) - self.items_so_far = nn.Buffer(torch.ones(1, dtype=dtype)) - - def forward(self, x: torch.Tensor) -> torch.Tensor: - if self.training: - # Welford's algorithm - self.running_mean.copy_( - (self.running_mean + (x - self.running_mean) / self.items_so_far).mean() - ) - self.items_so_far += 1 - - return x / self.running_mean - - -class _ALiBi(nn.Module): - # See MultiHeadAliBi - def __init__(self) -> None: - super().__init__() - - self.scale_distance = _RunningMeanScaler() - self.bias_scale = nn.Parameter(torch.rand(1)) - - def forward( - self, - *, - q: Float[Tensor, "batch query qk_feature"], - k: Float[Tensor, "batch key qk_feature"], - v: Float[Tensor, "batch key v_feature"], - coords_q: Float[Tensor, "batch query coord"], - coords_k: Float[Tensor, "batch key coord"], - attn_mask: Bool[Tensor, "batch query key"] | None, - alibi_mask: Bool[Tensor, "batch query key"] | None, - ) -> Float[Tensor, "batch query v_feature"]: - """ - Args: - alibi_mask: - Which query-key pairs to mask from ALiBi (i.e. don't apply ALiBi to). - """ - weight_logits = torch.einsum("bqf,bkf->bqk", q, k) * (k.size(-1) ** -0.5) - distances = torch.linalg.norm( - coords_q.unsqueeze(2) - coords_k.unsqueeze(1), dim=-1 - ) - scaled_distances = self.scale_distance(distances) * self.bias_scale - - if alibi_mask is not None: - scaled_distances = scaled_distances.where(~alibi_mask, 0.0) - - weights = torch.softmax(weight_logits, dim=-1) - - if attn_mask is not None: - weights = (weights - scaled_distances).where(~attn_mask, 0.0) - else: - weights = weights - scaled_distances - - attention = torch.einsum("bqk,bkf->bqf", weights, v) - - return attention - - -class MultiHeadALiBi(nn.Module): - """Attention with Linear Biases - - Based on - > PRESS, Ofir; SMITH, Noah A.; LEWIS, Mike. - > Train short, test long: Attention with linear biases enables input length extrapolation. - > arXiv preprint arXiv:2108.12409, 2021. - - Since the distances between in WSIs may be quite large, - we scale the distances by the mean distance seen during training. - """ - - def __init__( - self, - *, - embed_dim: int, - num_heads: int, - ) -> None: - super().__init__() - - if embed_dim % num_heads != 0: - raise ValueError(f"{embed_dim=} has to be divisible by {num_heads=}") - - self.query_encoders = nn.ModuleList( - [ - nn.Linear(in_features=embed_dim, out_features=embed_dim // num_heads) - for _ in range(num_heads) - ] - ) - self.key_encoders = nn.ModuleList( - [ - nn.Linear(in_features=embed_dim, out_features=embed_dim // num_heads) - for _ in range(num_heads) - ] - ) - self.value_encoders = nn.ModuleList( - [ - nn.Linear(in_features=embed_dim, out_features=embed_dim // num_heads) - for _ in range(num_heads) - ] - ) - - self.attentions = nn.ModuleList([_ALiBi() for _ in range(num_heads)]) - - self.fc = nn.Linear(in_features=embed_dim, out_features=embed_dim) - - def forward( - self, - *, - q: Float[Tensor, "batch query mh_qk_feature"], - k: Float[Tensor, "batch key mh_qk_feature"], - v: Float[Tensor, "batch key hm_v_feature"], - coords_q: Float[Tensor, "batch query coord"], - coords_k: Float[Tensor, "batch key coord"], - attn_mask: Bool[Tensor, "batch query key"] | None, - alibi_mask: Bool[Tensor, "batch query key"] | None, - ) -> Float[Tensor, "batch query mh_v_feature"]: - stacked_attentions = torch.stack( - [ - att( - q=q_enc(q), - k=k_enc(k), - v=v_enc(v), - coords_q=coords_q, - coords_k=coords_k, - attn_mask=attn_mask, - alibi_mask=alibi_mask, - ) - for q_enc, k_enc, v_enc, att in zip( - self.query_encoders, - self.key_encoders, - self.value_encoders, - self.attentions, - strict=True, - ) - ] - ) - return self.fc(stacked_attentions.permute(1, 2, 0, 3).flatten(-2, -1)) diff --git a/application/jobs/stamp/app/custom/crossval.py b/application/jobs/stamp/app/custom/crossval.py deleted file mode 100644 index 8f5148a9..00000000 --- a/application/jobs/stamp/app/custom/crossval.py +++ /dev/null @@ -1,226 +0,0 @@ -import logging -from collections.abc import Mapping, Sequence -from pathlib import Path -from typing import Any, Final - -import numpy as np -from lightning.pytorch.accelerators.accelerator import Accelerator -from pydantic import BaseModel -from sklearn.model_selection import StratifiedKFold - -from data import ( - PatientData, - filter_complete_patient_data_, - patient_to_ground_truth_from_clini_table_, - slide_to_patient_from_slide_table_, -) -from deploy import _predict, _to_prediction_df -from main import setup_model_for_training, train_model_ -from modeling.lightning_model import LitVisionTransformer -from modeling.types import ( - Category, - FeaturePath, - GroundTruth, - PandasLabel, - PatientId, -) -from transforms import VaryPrecisionTransform - -__author__ = "Marko van Treeck" -__copyright__ = "Copyright (C) 2024 Marko van Treeck" -__license__ = "MIT" - -_logger = logging.getLogger("stamp") - - -class _Split(BaseModel): - train_patients: set[PatientId] - test_patients: set[PatientId] - - -class _Splits(BaseModel): - splits: Sequence[_Split] - - -def categorical_crossval_( - clini_table: Path, - slide_table: Path, - feature_dir: Path, - output_dir: Path, - patient_label: PandasLabel, - ground_truth_label: PandasLabel, - filename_label: PandasLabel, - categories: Sequence[Category] | None, - n_splits: int, - # Dataset and -loader parameters - bag_size: int, - num_workers: int, - # Training paramenters - batch_size: int, - max_epochs: int, - patience: int, - accelerator: str | Accelerator, - # Experimental features - use_vary_precision_transform: bool, - use_alibi: bool, -) -> None: - patient_to_ground_truth: Final[dict[PatientId, GroundTruth]] = ( - patient_to_ground_truth_from_clini_table_( - clini_table_path=clini_table, - ground_truth_label=ground_truth_label, - patient_label=patient_label, - ) - ) - slide_to_patient: Final[dict[FeaturePath, PatientId]] = ( - slide_to_patient_from_slide_table_( - slide_table_path=slide_table, - feature_dir=feature_dir, - patient_label=patient_label, - filename_label=filename_label, - ) - ) - - # Clean data (remove slides without ground truth, missing features, etc.) - patient_to_data: Final[Mapping[Category, PatientData]] = ( - filter_complete_patient_data_( - patient_to_ground_truth=patient_to_ground_truth, - slide_to_patient=slide_to_patient, - drop_patients_with_missing_ground_truth=True, - ) - ) - - output_dir.mkdir(parents=True, exist_ok=True) - splits_file = output_dir / "splits.json" - - # Generate the splits, or load them from the splits file if they already exist - if not splits_file.exists(): - splits = _get_splits(patient_to_data=patient_to_data, n_splits=n_splits) - with open(splits_file, "w") as fp: - fp.write(splits.model_dump_json(indent=4)) - else: - _logger.debug(f"reading splits from {splits_file}") - with open(splits_file, "r") as fp: - splits = _Splits.model_validate_json(fp.read()) - - patients_in_splits = { - patient - for split in splits.splits - for patient in [*split.train_patients, *split.test_patients] - } - - if patients_without_ground_truth := patients_in_splits - patient_to_data.keys(): - raise RuntimeError( - "The splits file contains some patients we don't have information for in the clini / slide table: " - f"{patients_without_ground_truth}" - ) - - if ground_truths_not_in_split := patient_to_data.keys() - patients_in_splits: - _logger.warning( - "Some of the entries in the clini / slide table are not in the crossval split: " - f"{ground_truths_not_in_split}" - ) - - categories = categories or sorted( - { - patient_data.ground_truth - for patient_data in patient_to_data.values() - if patient_data.ground_truth is not None - } - ) - - for split_i, split in enumerate(splits.splits): - split_dir = output_dir / f"split-{split_i}" - - if (split_dir / "patient-preds.csv").exists(): - _logger.info( - f"skipping training for split {split_i}, " - "as a model checkpoint is already present" - ) - continue - - # Train the model - if not (split_dir / "model.ckpt").exists(): - model, train_dl, valid_dl = setup_model_for_training( - clini_table=clini_table, - slide_table=slide_table, - feature_dir=feature_dir, - ground_truth_label=ground_truth_label, - bag_size=bag_size, - num_workers=num_workers, - batch_size=batch_size, - patient_to_data={ - patient_id: patient_data - for patient_id, patient_data in patient_to_data.items() - if patient_id in split.train_patients - }, - categories=( - categories - or sorted( - { - patient_data.ground_truth - for patient_data in patient_to_data.values() - if patient_data.ground_truth is not None - } - ) - ), - train_transform=( - VaryPrecisionTransform(min_fraction_bits=1) - if use_vary_precision_transform - else None - ), - use_alibi=use_alibi, - ) - model = train_model_( - output_dir=split_dir, - model=model, - train_dl=train_dl, - valid_dl=valid_dl, - max_epochs=max_epochs, - patience=patience, - accelerator=accelerator, - ) - else: - model = LitVisionTransformer.load_from_checkpoint(split_dir / "model.ckpt") - - # Deploy on test set - if not (split_dir / "patient-preds.csv").exists(): - predictions = _predict( - model=model, - patient_to_data={ - patient_id: patient_data - for patient_id, patient_data in patient_to_data.items() - if patient_id in split.test_patients - }, - num_workers=num_workers, - accelerator=accelerator, - ) - - _to_prediction_df( - categories=categories, - patient_to_ground_truth=patient_to_ground_truth, - predictions=predictions, - patient_label=patient_label, - ground_truth_label=ground_truth_label, - ).to_csv(split_dir / "patient-preds.csv", index=False) - - -def _get_splits( - *, patient_to_data: Mapping[PatientId, PatientData[Any]], n_splits: int -) -> _Splits: - patients = np.array(list(patient_to_data.keys())) - skf = StratifiedKFold(n_splits=n_splits, shuffle=True, random_state=0) - splits = _Splits( - splits=[ - _Split( - train_patients=set(patients[train_indices]), - test_patients=set(patients[test_indices]), - ) - for train_indices, test_indices in skf.split( - patients, - np.array( - [patient.ground_truth for patient in patient_to_data.values()] - ), - ) - ] - ) - return splits diff --git a/application/jobs/stamp/app/custom/deploy.py b/application/jobs/stamp/app/custom/deploy.py deleted file mode 100644 index 235f7f8a..00000000 --- a/application/jobs/stamp/app/custom/deploy.py +++ /dev/null @@ -1,218 +0,0 @@ -import logging -from collections.abc import Mapping, Sequence -from pathlib import Path -from typing import TypeAlias, cast - -import lightning -import numpy as np -import pandas as pd -import torch -from jaxtyping import Float -from lightning.pytorch.accelerators.accelerator import Accelerator -from stamp.modeling.data import ( - PatientData, - dataloader_from_patient_data, - filter_complete_patient_data_, - patient_to_ground_truth_from_clini_table_, - slide_to_patient_from_slide_table_, -) -from stamp.modeling.lightning_model import LitVisionTransformer - -from modeling.types import GroundTruth, PandasLabel, PatientId - -__all__ = ["deploy_categorical_model_"] - -__author__ = "Marko van Treeck" -__copyright__ = "Copyright (C) 2024-2025 Marko van Treeck" -__license__ = "MIT" - -_logger = logging.getLogger("stamp") - -Logit: TypeAlias = float - - -def deploy_categorical_model_( - *, - output_dir: Path, - checkpoint_paths: Sequence[Path], - clini_table: Path | None, - slide_table: Path, - feature_dir: Path, - ground_truth_label: PandasLabel | None, - patient_label: PandasLabel, - filename_label: PandasLabel, - num_workers: int, - accelerator: str | Accelerator, -) -> None: - models = [ - LitVisionTransformer.load_from_checkpoint( - checkpoint_path=checkpoint_path - ).eval() - for checkpoint_path in checkpoint_paths - ] - - # Ensure all models were trained on the same ground truth label - if ( - len(ground_truth_labels := set(model.ground_truth_label for model in models)) - != 1 - ): - raise RuntimeError( - f"ground truth labels differ between models: {ground_truth_labels}" - ) - # Ensure the categories were the same between all models - if len(categories := set(tuple(model.categories) for model in models)) != 1: - raise RuntimeError(f"categories differ between models: {categories}") - - model_ground_truth_label = models[0].ground_truth_label - model_categories = list(models[0].categories) - - if ( - ground_truth_label is not None - and ground_truth_label != model_ground_truth_label - ): - _logger.warning( - "deployment ground truth label differs from training: " - f"{ground_truth_label} vs {model_ground_truth_label}" - ) - ground_truth_label = ground_truth_label or model_ground_truth_label - - output_dir.mkdir(exist_ok=True, parents=True) - - slide_to_patient = slide_to_patient_from_slide_table_( - slide_table_path=slide_table, - feature_dir=feature_dir, - patient_label=patient_label, - filename_label=filename_label, - ) - - patient_to_ground_truth: Mapping[PatientId, GroundTruth | None] - if clini_table is not None: - patient_to_ground_truth = patient_to_ground_truth_from_clini_table_( - clini_table_path=clini_table, - ground_truth_label=ground_truth_label, - patient_label=patient_label, - ) - else: - patient_to_ground_truth = { - patient_id: None for patient_id in set(slide_to_patient.values()) - } - - patient_to_data = filter_complete_patient_data_( - patient_to_ground_truth=patient_to_ground_truth, - slide_to_patient=slide_to_patient, - drop_patients_with_missing_ground_truth=False, - ) - - all_predictions: list[Mapping[PatientId, Float[torch.Tensor, "category"]]] = [] # noqa: F821 - for model_i, model in enumerate(models): - predictions = _predict( - model=model, - patient_to_data=patient_to_data, - num_workers=num_workers, - accelerator=accelerator, - ) - all_predictions.append(predictions) - - _to_prediction_df( - categories=model_categories, - patient_to_ground_truth=patient_to_ground_truth, - predictions=predictions, - patient_label=patient_label, - ground_truth_label=ground_truth_label, - ).to_csv(output_dir / f"patient-preds-{model_i}.csv", index=False) - - # TODO we probably also want to save the 95% confidence interval in addition to the mean - _to_prediction_df( - categories=model_categories, - patient_to_ground_truth=patient_to_ground_truth, - predictions={ - # Mean prediction - patient_id: torch.stack( - [predictions[patient_id] for predictions in all_predictions] - ).mean(dim=0) - for patient_id in patient_to_data.keys() - }, - patient_label=patient_label, - ground_truth_label=ground_truth_label, - ).to_csv(output_dir / "patient-preds.csv", index=False) - - -def _predict( - *, - model: LitVisionTransformer, - patient_to_data: Mapping[PatientId, PatientData[GroundTruth | None]], - num_workers: int, - accelerator: str | Accelerator, -) -> Mapping[PatientId, Float[torch.Tensor, "category"]]: # noqa: F821 - model = model.eval() - torch.set_float32_matmul_precision("medium") - - patients_used_for_training: set[PatientId] = set(model.train_patients) | set( - model.valid_patients - ) - if overlap := patients_used_for_training & set(patient_to_data.keys()): - raise ValueError( - f"some of the patients in the validation set were used during training: {overlap}" - ) - - test_dl, _ = dataloader_from_patient_data( - patient_data=list(patient_to_data.values()), - bag_size=None, # Use all the tiles for deployment - # Use same encoding scheme as during training - categories=list(model.categories), - batch_size=1, - shuffle=False, - num_workers=num_workers, - transform=None, - ) - - trainer = lightning.Trainer( - accelerator=accelerator, - devices=1, # Needs to be 1, otherwise half the predictions are missing for some reason - logger=False, - ) - predictions = torch.softmax( - torch.concat( - cast( - list[torch.Tensor], - trainer.predict(model, test_dl), - ) - ), - dim=1, - ) - - return dict(zip(patient_to_data, predictions, strict=True)) - - -def _to_prediction_df( - *, - categories: Sequence[GroundTruth], - patient_to_ground_truth: Mapping[PatientId, GroundTruth | None], - predictions: Mapping[PatientId, torch.Tensor], - patient_label: PandasLabel, - ground_truth_label: PandasLabel, -) -> pd.DataFrame: - """Compiles deployment results into a DataFrame.""" - return pd.DataFrame( - [ - { - patient_label: patient_id, - ground_truth_label: patient_to_ground_truth.get(patient_id), - "pred": categories[int(prediction.argmax())], - **{ - f"{ground_truth_label}_{category}": prediction[i_cat].item() - for i_cat, category in enumerate(categories) - }, - "loss": ( - torch.nn.functional.cross_entropy( - prediction.reshape(1, -1), - torch.tensor(np.where(np.array(categories) == ground_truth)[0]), - ).item() - if (ground_truth := patient_to_ground_truth.get(patient_id)) - is not None - else None - ), - } - for patient_id, prediction in predictions.items() - ] - ).sort_values(by="loss") From c0c27b1be564c09832ceaf83d7141f0b14e1bc7c Mon Sep 17 00:00:00 2001 From: GitHub CI Date: Tue, 19 Aug 2025 10:34:51 +0200 Subject: [PATCH 8/9] feat: enhance configuration and data loading for patient-level features Signed-off-by: GitHub CI --- application/jobs/stamp/app/custom/config.py | 64 ++++-- application/jobs/stamp/app/custom/data.py | 195 +++++++++++++++++- .../app/custom/modeling/lightning_model.py | 50 ++++- .../app/custom/modeling/mlp_classifier.py | 39 +++- 4 files changed, 311 insertions(+), 37 deletions(-) diff --git a/application/jobs/stamp/app/custom/config.py b/application/jobs/stamp/app/custom/config.py index 09fb4086..c4274211 100644 --- a/application/jobs/stamp/app/custom/config.py +++ b/application/jobs/stamp/app/custom/config.py @@ -1,10 +1,12 @@ import os +from collections.abc import Sequence from pathlib import Path import torch from pydantic import BaseModel, ConfigDict, Field -from modeling.types import PandasLabel +from modeling.registry import ModelName +from modeling.types import Category, PandasLabel class TrainConfig(BaseModel): @@ -13,28 +15,23 @@ class TrainConfig(BaseModel): output_dir: Path = Field(description="The directory to save the results to") clini_table: Path = Field(description="Excel or CSV to read clinical data from") - slide_table: Path = Field( - description="Excel or CSV to read patient-slide associations from" + slide_table: Path | None = Field( + default=None, description="Excel or CSV to read patient-slide associations from" ) feature_dir: Path = Field(description="Directory containing feature files") ground_truth_label: PandasLabel = Field( description="Name of categorical column in clinical table to train on" ) - categories: list[str] | None = None + categories: Sequence[Category] | None = None patient_label: PandasLabel = "PATIENT" filename_label: PandasLabel = "FILENAME" - # Dataset and -loader parameters - bag_size: int = 512 - num_workers: int = min(os.cpu_count() or 1, 16) - - # Training paramenters - batch_size: int = 64 - max_epochs: int = 64 - patience: int = 16 - accelerator: str = "gpu" if torch.cuda.is_available() else "cpu" + params_path: Path | None = Field( + default=None, + description="Optional: Path to a YAML file with advanced training parameters.", + ) # Experimental features use_vary_precision_transform: bool = False @@ -61,3 +58,44 @@ class DeploymentConfig(BaseModel): num_workers: int = min(os.cpu_count() or 1, 16) accelerator: str = "gpu" if torch.cuda.is_available() else "cpu" + + +class VitModelParams(BaseModel): + model_config = ConfigDict(extra="forbid") + dim_model: int = 512 + dim_feedforward: int = 512 + n_heads: int = 8 + n_layers: int = 2 + dropout: float = 0.0 + # Experimental feature: Use ALiBi positional embedding + use_alibi: bool = False + + +class MlpModelParams(BaseModel): + model_config = ConfigDict(extra="forbid") + dim_hidden: int = 512 + num_layers: int = 2 + dropout: float = 0.25 + + +class ModelParams(BaseModel): + model_config = ConfigDict(extra="forbid") + vit: VitModelParams + mlp: MlpModelParams + + +class AdvancedConfig(BaseModel): + model_config = ConfigDict(extra="forbid") + bag_size: int = 512 + num_workers: int = min(os.cpu_count() or 1, 16) + batch_size: int = 64 + max_epochs: int = 32 + patience: int = 16 + accelerator: str = "gpu" if torch.cuda.is_available() else "cpu" + max_lr: float = 1e-4 + div_factor: float = 25.0 + model_name: ModelName | None = Field( + default=None, + description='Optional: "vit" or "mlp". Defaults based on feature type.', + ) + model_params: ModelParams diff --git a/application/jobs/stamp/app/custom/data.py b/application/jobs/stamp/app/custom/data.py index b7f51b0e..52ab10c8 100755 --- a/application/jobs/stamp/app/custom/data.py +++ b/application/jobs/stamp/app/custom/data.py @@ -5,13 +5,14 @@ from dataclasses import KW_ONLY, dataclass from itertools import groupby from pathlib import Path -from typing import BinaryIO, Generic, TextIO, TypeAlias, cast +from typing import IO, BinaryIO, Generic, TextIO, TypeAlias, cast, Union import h5py import numpy as np import pandas as pd import torch from jaxtyping import Bool, Float +from packaging.version import Version from torch import Tensor from torch.utils.data import DataLoader, Dataset @@ -33,6 +34,8 @@ ) _logger = logging.getLogger("stamp") +_logged_stamp_v1_warning = False + __author__ = "Marko van Treeck" __copyright__ = "Copyright (C) 2022-2025 Marko van Treeck" @@ -40,6 +43,7 @@ _Bag: TypeAlias = Float[Tensor, "tile feature"] _EncodedTarget: TypeAlias = Bool[Tensor, "category_is_hot"] # noqa: F821 +_BinaryIOLike: TypeAlias = Union[BinaryIO, IO[bytes]] """The ground truth, encoded numerically (currently: one-hot)""" _Coordinates: TypeAlias = Float[Tensor, "tile 2"] @@ -53,7 +57,7 @@ class PatientData(Generic[GroundTruthType]): feature_files: Iterable[FeaturePath | BinaryIO] -def dataloader_from_patient_data( +def tile_bag_dataloader( *, patient_data: Sequence[PatientData[GroundTruth | None]], bag_size: int | None, @@ -66,7 +70,7 @@ def dataloader_from_patient_data( DataLoader[tuple[Bags, CoordinatesBatch, BagSizes, EncodedTargets]], Sequence[Category], ]: - """Creates a dataloader from patient data, encoding the ground truths. + """Creates a dataloader from patient data for tile-level (bagged) features. Args: categories: @@ -112,12 +116,115 @@ def _collate_to_tuple( return (bags, coords, bag_sizes, encoded_targets) +def patient_feature_dataloader( + *, + patient_data: Sequence[PatientData[GroundTruth | None]], + categories: Sequence[Category] | None = None, + batch_size: int, + shuffle: bool, + num_workers: int, + transform: Callable[[Tensor], Tensor] | None, +) -> tuple[DataLoader, Sequence[Category]]: + """ + Creates a dataloader for patient-level features (one feature vector per patient). + """ + feature_files = [next(iter(p.feature_files)) for p in patient_data] + raw_ground_truths = np.array([patient.ground_truth for patient in patient_data]) + categories = ( + categories if categories is not None else list(np.unique(raw_ground_truths)) + ) + one_hot = torch.tensor(raw_ground_truths.reshape(-1, 1) == categories) + ds = PatientFeatureDataset(feature_files, one_hot, transform=transform) + dl = DataLoader(ds, batch_size=batch_size, shuffle=shuffle, num_workers=num_workers) + return dl, categories + + +def detect_feature_type(feature_dir: Path) -> str: + """ + Detects feature type by inspecting all .h5 files in feature_dir. + + Returns: + "tile" if all files are tile-level, "patient" if all are patient-level. + If files have mixed types, raises an error. + If no .h5 files are found, raises an error. + """ + feature_types = set() + files_checked = 0 + + for file in feature_dir.glob("*.h5"): + files_checked += 1 + with h5py.File(file, "r") as h5: + feat_type = h5.attrs.get("feat_type") + encoder = h5.attrs.get("encoder") + + if feat_type is not None or encoder is not None: + feature_types.add(str(feat_type)) + else: + # If feat_type is missing, always treat as tile-level feature + feature_types.add("tile") + + if files_checked == 0: + raise RuntimeError("No .h5 feature files found in feature_dir.") + + if len(feature_types) > 1: + raise RuntimeError( + f"Multiple feature types detected in {feature_dir}: {feature_types}. " + "All feature files must have the same type." + ) + + return feature_types.pop() + + +def load_patient_level_data( + *, + clini_table: Path, + feature_dir: Path, + patient_label: PandasLabel, + ground_truth_label: PandasLabel, + feature_ext: str = ".h5", +) -> dict[PatientId, PatientData]: + """ + Loads PatientData for patient-level features, matching patients in the clinical table + to feature files in feature_dir named {patient_id}.h5. + """ + # TODO: I'm not proud at all of this. Any other alternative for mapping + # clinical data to the patient-level feature paths that avoids + # creating another slide table for encoded featuress is welcome :P. + + clini_df = read_table( + clini_table, + usecols=[patient_label, ground_truth_label], + dtype=str, + ).dropna() + + patient_to_data: dict[PatientId, PatientData] = {} + missing_features = [] + for _, row in clini_df.iterrows(): + patient_id = PatientId(str(row[patient_label])) + ground_truth = row[ground_truth_label] + feature_file = feature_dir / f"{patient_id}{feature_ext}" + if feature_file.exists(): + patient_to_data[patient_id] = PatientData( + ground_truth=ground_truth, + feature_files=[FeaturePath(feature_file)], + ) + else: + missing_features.append(patient_id) + + if missing_features: + _logger.warning( + f"Some patients have no feature file in {feature_dir}: {missing_features}" + ) + + return patient_to_data + + @dataclass class BagDataset(Dataset[tuple[_Bag, _Coordinates, BagSize, _EncodedTarget]]): """A dataset of bags of instances.""" _: KW_ONLY - bags: Sequence[Iterable[FeaturePath | BinaryIO]] + bags: Sequence[Iterable[FeaturePath | _BinaryIOLike]] """The `.h5` files containing the bags. Each bag consists of the features taken from one or multiple h5 files. @@ -182,6 +289,47 @@ def __getitem__( ) +class PatientFeatureDataset(Dataset): + """ + Dataset for single feature vector per sample (e.g. slide-level or patient-level). + Each item is a (feature_vector, label_onehot) tuple. + """ + + def __init__( + self, + feature_files: Sequence[FeaturePath | BinaryIO], + ground_truths: Tensor, # shape: [num_samples, num_classes] + transform: Callable[[Tensor], Tensor] | None, + ): + if len(feature_files) != len(ground_truths): + raise ValueError("Number of feature files and ground truths must match.") + self.feature_files = feature_files + self.ground_truths = ground_truths + self.transform = transform + + def __len__(self): + return len(self.feature_files) + + def __getitem__(self, idx: int): + feature_file = self.feature_files[idx] + with h5py.File(feature_file, "r") as h5: + feats = torch.from_numpy(h5["feats"][:]) # pyright: ignore[reportIndexIssue] + # Accept [V] or [1, V] + if feats.ndim == 2 and feats.shape[0] == 1: + feats = feats[0] + elif feats.ndim == 1: + pass + else: + raise RuntimeError( + f"Expected single feature vector (shape [F] or [1, F]), got {feats.shape} in {feature_file}." + "Check that the features are patient-level." + ) + if self.transform is not None: + feats = self.transform(feats) + label = self.ground_truths[idx] + return feats, label + + @dataclass class CoordsInfo: coords_um: np.ndarray @@ -221,13 +369,24 @@ def get_coords(feature_h5: h5py.File) -> CoordsInfo: == 224 ): # Historic STAMP format - _logger.info( - f"{feature_h5.filename}: tile stride is roughly 224, assuming coordinates have unit 256um/224px (historic STAMP format)" - ) + # TODO: find a better way to get this warning just once + global _logged_stamp_v1_warning + if not _logged_stamp_v1_warning: + _logger.info( + f"{feature_h5.filename}: tile stride is roughly 224, assuming coordinates have unit 256um/224px (historic STAMP format)" + ) + _logged_stamp_v1_warning = True tile_size_um = Microns(256.0) tile_size_px = TilePixels(224) coords_um = coords / 224 * 256 + if (version_str := feature_h5.attrs.get("stamp_version")) and ( + extraction_version := Version(version_str) + ) > Version(stamp.__version__): + raise RuntimeError( + f"features were extracted with a newer version of stamp, please update your stamp to at least version {extraction_version}." + ) + if not tile_size_px and "tile_size_px" in feature_h5.attrs: tile_size_px = TilePixels(int(feature_h5.attrs["tile_size_px"])) # pyright: ignore[reportArgumentType] @@ -276,7 +435,7 @@ def patient_to_ground_truth_from_clini_table_( ground_truth_label: PandasLabel, ) -> dict[PatientId, GroundTruth]: """Loads the patients and their ground truths from a clini table.""" - clini_df = _read_table( + clini_df = read_table( clini_table_path, usecols=[patient_label, ground_truth_label], dtype=str, @@ -309,12 +468,26 @@ def slide_to_patient_from_slide_table_( patient_label: PandasLabel, filename_label: PandasLabel, ) -> dict[FeaturePath, PatientId]: - """Creates a slide-to-patient mapping from a slide table.""" - slide_df = _read_table( + """ + Creates a slide-to-patient mapping from a slide table. + Side effects: + Verifies that all files in the slide tables filename_label + column has an .h5 extension. + """ + slide_df = read_table( slide_table_path, usecols=[patient_label, filename_label], dtype=str, ) + # Verify the slide table contains a feature path with .h5 extension by + # checking the filename_label. + for x in slide_df[filename_label]: + if not str(x).endswith(".h5"): + raise ValueError( + "One or more files are missing the .h5 extension in the " + "filename_label column. The first file missing the .h5 " + "extension is: " + str(x) + "." + ) slide_to_patient: Mapping[FeaturePath, PatientId] = { FeaturePath(feature_dir / cast(str, k)): PatientId(cast(str, patient)) @@ -326,7 +499,7 @@ def slide_to_patient_from_slide_table_( return slide_to_patient -def _read_table(path: Path | TextIO, **kwargs) -> pd.DataFrame: +def read_table(path: Path | TextIO, **kwargs) -> pd.DataFrame: if not isinstance(path, Path): return pd.read_csv(path, **kwargs) elif path.suffix == ".xlsx": diff --git a/application/jobs/stamp/app/custom/modeling/lightning_model.py b/application/jobs/stamp/app/custom/modeling/lightning_model.py index 2dd5f64e..c45f9d6e 100644 --- a/application/jobs/stamp/app/custom/modeling/lightning_model.py +++ b/application/jobs/stamp/app/custom/modeling/lightning_model.py @@ -7,6 +7,7 @@ import numpy as np import torch from jaxtyping import Bool, Float +from packaging.version import Version from torch import Tensor, nn, optim from torchmetrics.classification import MulticlassAUROC @@ -30,13 +31,12 @@ class LitVisionTransformer(lightning.LightningModule): learning settings, such as Multiple Instance Learning (MIL) for whole-slide images or patch-based data. This class encapsulates training, validation, testing, and prediction logic, along with: - - Masking logic that ensures only valid tiles (patches) participate in attention during training. + - Masking logic that ensures only valid tiles (patches) participate in attention during training (deactivated) - AUROC metric tracking during validation for multiclass classification. - Compatibility checks based on the `stamp` framework version. - Integration of class imbalance handling through weighted cross-entropy loss. - The attention mask is applied *only* during training to hide paddings - and is skipped during evaluation and inference for reducing memory usage. + The attention mask is currently deactivated to reduce memory usage. Args: categories: List of class labels. @@ -47,6 +47,9 @@ class LitVisionTransformer(lightning.LightningModule): n_heads: Number of self-attention heads. n_layers: Number of transformer layers. dropout: Dropout rate used throughout the model. + total_steps: Number of steps done in the LR Scheduler cycle. + max_lr: max learning rate. + div_factor: Determines the initial learning rate via initial_lr = max_lr/div_factor use_alibi: Whether to use ALiBi-style positional bias in attention (optional). ground_truth_label: Column name for accessing ground-truth labels from metadata. train_patients: List of patient IDs used for training. @@ -68,9 +71,12 @@ def __init__( n_heads: int, n_layers: int, dropout: float, + # Learning Rate Scheduler params, not used in inference + total_steps: int, + max_lr: float, + div_factor: float, # Experimental features - # TODO remove default values for stamp 3; they're only here for backwards compatibility - use_alibi: bool = False, + use_alibi: bool, # Metadata used by other parts of stamp, but not by the model itself ground_truth_label: PandasLabel, train_patients: Iterable[PatientId], @@ -97,6 +103,9 @@ def __init__( ) self.class_weights = category_weights self.valid_auroc = MulticlassAUROC(len(categories)) + self.total_steps = total_steps + self.max_lr = max_lr + self.div_factor = div_factor # Used during deployment self.ground_truth_label = ground_truth_label @@ -160,7 +169,7 @@ def training_step( batch: tuple[Bags, CoordinatesBatch, BagSizes, EncodedTargets], batch_idx: int, ) -> Loss: - return self._step(batch=batch, step_name="training", use_mask=True) + return self._step(batch=batch, step_name="training", use_mask=False) def validation_step( self, @@ -185,9 +194,32 @@ def predict_step( # adding a mask here will *drastically* and *unbearably* increase memory usage return self.vision_transformer(bags, coords=coords, mask=None) - def configure_optimizers(self) -> optim.Optimizer: - optimizer = optim.Adam(self.parameters(), lr=1e-3) - return optimizer + def configure_optimizers( + self, + ) -> tuple[list[optim.Optimizer], list[optim.lr_scheduler.LRScheduler]]: + optimizer = optim.AdamW( + self.parameters(), lr=1e-3 + ) # this lr value should be ignored with the scheduler + + scheduler = optim.lr_scheduler.OneCycleLR( + optimizer=optimizer, + total_steps=self.total_steps, + max_lr=self.max_lr, + div_factor=self.div_factor, + ) + return [optimizer], [scheduler] + + def on_train_batch_end(self, outputs, batch, batch_idx): + # Log learning rate at the end of each training batch + current_lr = self.trainer.optimizers[0].param_groups[0]["lr"] + self.log( + "learning_rate", + current_lr, + on_step=False, + on_epoch=True, + prog_bar=True, + sync_dist=True, + ) def _mask_from_bags( diff --git a/application/jobs/stamp/app/custom/modeling/mlp_classifier.py b/application/jobs/stamp/app/custom/modeling/mlp_classifier.py index d99ec5e7..0bf13517 100644 --- a/application/jobs/stamp/app/custom/modeling/mlp_classifier.py +++ b/application/jobs/stamp/app/custom/modeling/mlp_classifier.py @@ -3,7 +3,8 @@ import lightning import numpy as np import torch -from torch import Tensor, nn +from packaging.version import Version +from torch import Tensor, nn, optim from torchmetrics.classification import MulticlassAUROC from .types import Category, PandasLabel, PatientId @@ -56,6 +57,10 @@ def __init__( ground_truth_label: PandasLabel, train_patients: Iterable[PatientId], valid_patients: Iterable[PatientId], + # Learning Rate Scheduler params, used only in training + total_steps: int, + max_lr: float, + div_factor: float, **metadata, ): super().__init__() @@ -73,8 +78,10 @@ def __init__( self.categories = np.array(categories) self.train_patients = train_patients self.valid_patients = valid_patients + self.total_steps = total_steps + self.max_lr = max_lr + self.div_factor = div_factor - # TODO: Add version check with version 2.2.1, for both MLP and Transformer def forward(self, x: Tensor) -> Tensor: return self.model(x) @@ -119,5 +126,29 @@ def predict_step(self, batch, batch_idx): feats, _ = batch return self.model(feats) - def configure_optimizers(self): - return torch.optim.Adam(self.parameters(), lr=1e-3) + def configure_optimizers( + self, + ) -> tuple[list[optim.Optimizer], list[optim.lr_scheduler.LRScheduler]]: + optimizer = optim.AdamW( + self.parameters(), lr=1e-3 + ) # this lr value should be ignored with the scheduler + + scheduler = optim.lr_scheduler.OneCycleLR( + optimizer=optimizer, + total_steps=self.total_steps, + max_lr=self.max_lr, + div_factor=25.0, + ) + return [optimizer], [scheduler] + + def on_train_batch_end(self, outputs, batch, batch_idx): + # Log learning rate at the end of each training batch + current_lr = self.trainer.optimizers[0].param_groups[0]["lr"] + self.log( + "learning_rate", + current_lr, + on_step=False, + on_epoch=True, + prog_bar=True, + sync_dist=True, + ) From 1bea4163d8591bed17f1dca86761e4b2a618a132 Mon Sep 17 00:00:00 2001 From: GitHub CI Date: Tue, 19 Aug 2025 14:07:09 +0200 Subject: [PATCH 9/9] feat: add configuration classes for training and deployment settings Signed-off-by: GitHub CI --- application/jobs/stamp/app/custom/main.py | 809 +++++++----------- .../jobs/stamp/app/custom/modeling/config.py | 100 +++ docker_config/Dockerfile_stamp | 2 +- 3 files changed, 433 insertions(+), 478 deletions(-) create mode 100644 application/jobs/stamp/app/custom/modeling/config.py diff --git a/application/jobs/stamp/app/custom/main.py b/application/jobs/stamp/app/custom/main.py index 107cf0de..c42220cf 100644 --- a/application/jobs/stamp/app/custom/main.py +++ b/application/jobs/stamp/app/custom/main.py @@ -1,23 +1,6 @@ import logging -import sys - -from torch import tensor - -import modeling.lightning_model -from modeling.registry import MODEL_REGISTRY - -print("Training script is using:", modeling.lightning_model.__file__) - -logger = logging.getLogger("test") -logger.setLevel(logging.DEBUG) - -if not logger.hasHandlers(): - handler = logging.StreamHandler(sys.stderr) - handler.setFormatter(logging.Formatter("%(asctime)s\t%(levelname)s\t%(message)s")) - handler.setLevel(logging.DEBUG) - logger.addHandler(handler) - import shutil +from collections import Counter from collections.abc import Callable, Mapping, Sequence from pathlib import Path from typing import cast @@ -26,136 +9,326 @@ import lightning.pytorch import lightning.pytorch.accelerators import lightning.pytorch.accelerators.accelerator +import nvflare.client as flare_util +import nvflare.client.lightning as flare import torch from lightning.pytorch.accelerators.accelerator import Accelerator -from lightning.pytorch.callbacks import ModelCheckpoint +from lightning.pytorch.callbacks import EarlyStopping, ModelCheckpoint from lightning.pytorch.loggers import CSVLogger - from sklearn.model_selection import train_test_split from torch.utils.data.dataloader import DataLoader -import logging -import nvflare.client.lightning as flare -import nvflare.client as flare_util -from data import ( - BagDataset, - PatientData, - dataloader_from_patient_data, - filter_complete_patient_data_, - patient_to_ground_truth_from_clini_table_, - slide_to_patient_from_slide_table_, -) +from modeling.config import AdvancedConfig, TrainConfig from modeling.lightning_model import ( Bags, BagSizes, EncodedTargets, - LitVisionTransformer, ) -from transforms import VaryPrecisionTransform +from modeling.registry import MODEL_REGISTRY, ModelName from modeling.types import Category, CoordinatesBatch, GroundTruth, PandasLabel, PatientId +from .data import ( + BagDataset, + PatientData, + PatientFeatureDataset, + detect_feature_type, + filter_complete_patient_data_, + load_patient_level_data, + patient_feature_dataloader, + patient_to_ground_truth_from_clini_table_, + slide_to_patient_from_slide_table_, + tile_bag_dataloader, +) +from .transforms import VaryPrecisionTransform + +__author__ = "Marko van Treeck" +__copyright__ = "Copyright (C) 2024 Marko van Treeck" +__license__ = "MIT" + +_logger = logging.getLogger("stamp") def train_categorical_model_( *, - clini_table: Path, - slide_table: Path, - feature_dir: Path, - output_dir: Path, - patient_label: PandasLabel, - ground_truth_label: PandasLabel, - filename_label: PandasLabel, - categories: Sequence[Category] | None, - # Dataset and -loader parameters - bag_size: int, - num_workers: int, - # Training paramenters - batch_size: int, - max_epochs: int, - patience: int, - accelerator: str | Accelerator, - # Experimental features - use_vary_precision_transform: bool, - use_alibi: bool, + config: TrainConfig, + advanced: AdvancedConfig, ) -> None: - """Trains a model. - - Args: - clini_table: - An excel or csv file to read the clinical information from. - Must at least have the columns specified in the arguments - - `patient_label` (containing a unique patient ID) - and `ground_truth_label` (containing the ground truth to train for). - slide_table: - An excel or csv file to read the patient-slide associations from. - Must at least have the columns specified in the arguments - `patient_label` (containing the patient ID) - and `filename_label` - (containing a filename relative to `feature_dir` - in which some of the patient's features are stored). - feature_dir: - See `slide_table`. - output_dir: - Path into which to output the artifacts (trained model etc.) - generated during training. - patient_label: - See `clini_table`, `slide_table`. - ground_truth_label: - See `clini_table`. - filename_label: - See `slide_table`. - categories: - Categories of the ground truth. - Set to `None` to automatically infer. - """ - # Read and parse data from out clini and slide table - patient_to_ground_truth = patient_to_ground_truth_from_clini_table_( - clini_table_path=clini_table, - ground_truth_label=ground_truth_label, - patient_label=patient_label, - ) - slide_to_patient = slide_to_patient_from_slide_table_( - slide_table_path=slide_table, - feature_dir=feature_dir, - patient_label=patient_label, - filename_label=filename_label, - ) - - # Clean data (remove slides without ground truth, missing features, etc.) - patient_to_data = filter_complete_patient_data_( - patient_to_ground_truth=patient_to_ground_truth, - slide_to_patient=slide_to_patient, - drop_patients_with_missing_ground_truth=True, - ) + """Trains a model based on the feature type.""" + feature_type = detect_feature_type(config.feature_dir) + _logger.info(f"Detected feature type: {feature_type}") + + if feature_type == "tile": + if config.slide_table is None: + raise ValueError("A slide table is required for tile-level modeling") + patient_to_ground_truth = patient_to_ground_truth_from_clini_table_( + clini_table_path=config.clini_table, + ground_truth_label=config.ground_truth_label, + patient_label=config.patient_label, + ) + slide_to_patient = slide_to_patient_from_slide_table_( + slide_table_path=config.slide_table, + feature_dir=config.feature_dir, + patient_label=config.patient_label, + filename_label=config.filename_label, + ) + patient_to_data = filter_complete_patient_data_( + patient_to_ground_truth=patient_to_ground_truth, + slide_to_patient=slide_to_patient, + drop_patients_with_missing_ground_truth=True, + ) + elif feature_type == "patient": + # Patient-level: ignore slide_table + if config.slide_table is not None: + _logger.warning("slide_table is ignored for patient-level features.") + patient_to_data = load_patient_level_data( + clini_table=config.clini_table, + feature_dir=config.feature_dir, + patient_label=config.patient_label, + ground_truth_label=config.ground_truth_label, + ) + elif feature_type == "slide": + raise RuntimeError( + "Slide-level features are not supported for training." + "Please rerun the encoding step with patient-level encoding." + ) + else: + raise RuntimeError(f"Unknown feature type: {feature_type}") - # Train the model + # Train the model (the rest of the logic is unchanged) model, train_dl, valid_dl = setup_model_for_training( patient_to_data=patient_to_data, - categories=categories, - bag_size=bag_size, - batch_size=batch_size, - num_workers=num_workers, - ground_truth_label=ground_truth_label, - clini_table=clini_table, - slide_table=slide_table, - feature_dir=feature_dir, + categories=config.categories, + advanced=advanced, + ground_truth_label=config.ground_truth_label, + clini_table=config.clini_table, + slide_table=config.slide_table, + feature_dir=config.feature_dir, train_transform=( VaryPrecisionTransform(min_fraction_bits=1) - if use_vary_precision_transform + if config.use_vary_precision_transform else None ), - use_alibi=use_alibi, + feature_type=feature_type, ) train_model_( - output_dir=output_dir, + output_dir=config.output_dir, model=model, train_dl=train_dl, valid_dl=valid_dl, - max_epochs=max_epochs, - patience=patience, - accelerator=accelerator, + max_epochs=advanced.max_epochs, + patience=advanced.patience, + accelerator=advanced.accelerator, + ) + + +def setup_model_for_training( + *, + patient_to_data: Mapping[PatientId, PatientData[GroundTruth]], + categories: Sequence[Category] | None, + train_transform: Callable[[torch.Tensor], torch.Tensor] | None, + feature_type: str, + advanced: AdvancedConfig, + # Metadata, has no effect on model training + ground_truth_label: PandasLabel, + clini_table: Path, + slide_table: Path | None, + feature_dir: Path, +) -> tuple[ + lightning.LightningModule, + DataLoader, + DataLoader, +]: + """Creates a model and dataloaders for training""" + + train_dl, valid_dl, train_categories, dim_feats, train_patients, valid_patients = ( + setup_dataloaders_for_training( + patient_to_data=patient_to_data, + categories=categories, + bag_size=advanced.bag_size, + batch_size=advanced.batch_size, + num_workers=advanced.num_workers, + train_transform=train_transform, + feature_type=feature_type, + ) + ) + + _logger.info( + "Training dataloaders: bag_size=%s, batch_size=%s, num_workers=%s", + advanced.bag_size, + advanced.batch_size, + advanced.num_workers, ) + category_weights = _compute_class_weights_and_check_categories( + train_dl=train_dl, + feature_type=feature_type, + train_categories=train_categories, + ) + + # 1. Default to a model if none is specified + if advanced.model_name is None: + advanced.model_name = ModelName.VIT if feature_type == "tile" else ModelName.MLP + _logger.info( + f"No model specified, defaulting to '{advanced.model_name.value}' for feature type '{feature_type}'" + ) + + # 2. Validate that the chosen model supports the feature type + model_info = MODEL_REGISTRY[advanced.model_name] + if feature_type not in model_info["supported_features"]: + raise ValueError( + f"Model '{advanced.model_name.value}' does not support feature type '{feature_type}'. " + f"Supported types are: {model_info['supported_features']}" + ) + + # 3. Get model-specific hyperparameters + model_specific_params = advanced.model_params.model_dump()[ + advanced.model_name.value + ] + + # 4. Calculate total steps for scheduler + steps_per_epoch = len(train_dl) + total_steps = steps_per_epoch * advanced.max_epochs + + # 5. Prepare common parameters + common_params = { + "categories": train_categories, + "category_weights": category_weights, + "dim_input": dim_feats, + "total_steps": total_steps, + "max_lr": advanced.max_lr, + "div_factor": advanced.div_factor, + # Metadata, has no effect on model training + "model_name": advanced.model_name.value, + "ground_truth_label": ground_truth_label, + "train_patients": train_patients, + "valid_patients": valid_patients, + "clini_table": clini_table, + "slide_table": slide_table, + "feature_dir": feature_dir, + } + + # 6. Instantiate the model dynamically + ModelClass = model_info["model_class"] + all_params = {**common_params, **model_specific_params} + _logger.info( + f"Instantiating model '{advanced.model_name.value}' with parameters: {model_specific_params}" + ) + _logger.info( + "Other params: max_epochs=%s, patience=%s", + advanced.max_epochs, + advanced.patience, + ) + model = ModelClass(**all_params) + + return model, train_dl, valid_dl + + +def setup_dataloaders_for_training( + *, + patient_to_data: Mapping[PatientId, PatientData[GroundTruth]], + categories: Sequence[Category] | None, + bag_size: int, + batch_size: int, + num_workers: int, + train_transform: Callable[[torch.Tensor], torch.Tensor] | None, + feature_type: str, +) -> tuple[ + DataLoader, + DataLoader, + Sequence[Category], + int, + Sequence[PatientId], + Sequence[PatientId], +]: + """ + Creates train/val dataloaders for tile-level or patient-level features. + + Returns: + train_dl, valid_dl, categories, feature_dim, train_patients, valid_patients + """ + # Sample count for training + log_total_class_summary(patient_to_data, categories) + + # Stratified split + ground_truths = [ + patient_data.ground_truth + for patient_data in patient_to_data.values() + if patient_data.ground_truth is not None + ] + if len(ground_truths) != len(patient_to_data): + raise ValueError( + "patient_to_data must have a ground truth defined for all targets!" + ) + + train_patients, valid_patients = cast( + tuple[Sequence[PatientId], Sequence[PatientId]], + train_test_split( + list(patient_to_data), stratify=ground_truths, shuffle=True, random_state=0 + ), + ) + + if feature_type == "tile": + # Use existing BagDataset logic + train_dl, train_categories = tile_bag_dataloader( + patient_data=[patient_to_data[pid] for pid in train_patients], + categories=categories, + bag_size=bag_size, + batch_size=batch_size, + shuffle=True, + num_workers=num_workers, + transform=train_transform, + ) + valid_dl, _ = tile_bag_dataloader( + patient_data=[patient_to_data[pid] for pid in valid_patients], + bag_size=None, + categories=train_categories, + batch_size=1, + shuffle=False, + num_workers=num_workers, + transform=None, + ) + bags, _, _, _ = next(iter(train_dl)) + dim_feats = bags.shape[-1] + return ( + train_dl, + valid_dl, + train_categories, + dim_feats, + train_patients, + valid_patients, + ) + + elif feature_type == "patient": + train_dl, train_categories = patient_feature_dataloader( + patient_data=[patient_to_data[pid] for pid in train_patients], + categories=categories, + batch_size=batch_size, + shuffle=True, + num_workers=num_workers, + transform=train_transform, + ) + valid_dl, _ = patient_feature_dataloader( + patient_data=[patient_to_data[pid] for pid in valid_patients], + categories=train_categories, + batch_size=1, + shuffle=False, + num_workers=num_workers, + transform=None, + ) + feats, _ = next(iter(train_dl)) + dim_feats = feats.shape[-1] + return ( + train_dl, + valid_dl, + train_categories, + dim_feats, + train_patients, + valid_patients, + ) + else: + raise RuntimeError( + f"Unsupported feature type: {feature_type}. Only 'tile' and 'patient' are supported." + ) + def train_model_( *, @@ -179,13 +352,19 @@ def train_model_( mode="min", filename="checkpoint-{epoch:02d}-{validation_loss:0.3f}", ) + trainer = lightning.Trainer( default_root_dir=output_dir, callbacks=[ - # EarlyStopping(monitor="validation_loss", mode="min", patience=patience), + EarlyStopping(monitor="validation_loss", mode="min", patience=patience), model_checkpoint, ], max_epochs=max_epochs, + # FIXME The number of accelerators is currently fixed to one for the + # following reasons: + # 1. `trainer.predict()` does not return any predictions if used with + # the default strategy no multiple GPUs + # 2. `barspoon.model.SafeMulticlassAUROC` breaks on multiple GPUs accelerator=accelerator, devices=1, gradient_clip_val=0.5, @@ -195,47 +374,8 @@ def train_model_( flare_util.init() SITE_NAME = flare.get_site_name() flare.patch(trainer) # Patch trainer to enable swarm learning - logger.info(f"Site name: {SITE_NAME}") - - logger.info(" About to enter flare.is_running loop") - while flare.is_running(): - logger.info("[DEBUG] waiting to receive swarm model (10s timeout)") input_model = flare.receive() - # logger.info('ModelClass state dict keys()', model.state_dict().keys()) - ''' - if input_model is not None: - logger.info("==== Swarm model received ====") - logger.info( - f"input_model.params.keys() = {list(input_model.params.keys())[:10]} ... total = {len(input_model.params)}") - # log input_model.params - logger.info(f"input_model.params.keys() = {input_model.params.keys()}") - logger.info( - f"model.state_dict().keys() = {list(model.state_dict().keys())[:10]} ... total = {len(model.state_dict())}") - try: - model.load_state_dict(input_model.params) - except Exception as e: - logger.error("load_state_dict failed:", exc_info=True) - raise - - logger.info(f"[DEBUG] Got input model: {input_model}") - if input_model is None: - logger.info("[DEBUG] no swarm_start received in 10s") - else: - model.load_state_dict(input_model.params) - - logger.info("[DEBUG] received swarm_start:", input_model) - logger.info(f"Current round: {input_model.current_round}") - ''' - - logger.info(f"input_model.params.keys() = {input_model.params.keys()}") - logger.info( - f"model.state_dict().keys() = {list(model.state_dict().keys())[:10]} ... total = {len(model.state_dict())}") - # log type(model) - logger.info(f"model class: {type(model)}") - - #model = input_model.model - trainer.fit( model=model, train_dataloaders=train_dl, @@ -243,81 +383,28 @@ def train_model_( ) shutil.copy(model_checkpoint.best_model_path, output_dir / "model.ckpt") - return LitVisionTransformer.load_from_checkpoint(model_checkpoint.best_model_path) + # Reload the best model using the same class as the input model + ModelClass = type(model) + return ModelClass.load_from_checkpoint(model_checkpoint.best_model_path) -def setup_model_for_training( +def _compute_class_weights_and_check_categories( *, - patient_to_data: Mapping[PatientId, PatientData[GroundTruth]], - categories: Sequence[Category] | None, - bag_size: int, - batch_size: int, - num_workers: int, - train_transform: Callable[[torch.Tensor], torch.Tensor] | None, - use_alibi: bool, - # Metadata, has no effect on model training - ground_truth_label: PandasLabel, - clini_table: Path, - slide_table: Path, - feature_dir: Path, -) -> tuple[ - lightning.LightningModule, - DataLoader[tuple[Bags, CoordinatesBatch, BagSizes, EncodedTargets]], - DataLoader[tuple[Bags, CoordinatesBatch, BagSizes, EncodedTargets]], -]: - """Creates a model and dataloaders for training""" - - # Do a stratified train-validation split - ground_truths = [ - patient_data.ground_truth - for patient_data in patient_to_data.values() - if patient_data.ground_truth is not None - ] - - if len(ground_truths) != len(patient_to_data): - raise ValueError( - "patient_to_data must have a ground truth defined for all targets!" - ) - - train_patients, valid_patients = cast( - tuple[Sequence[PatientId], Sequence[PatientId]], - train_test_split( - list(patient_to_data), stratify=ground_truths, shuffle=True, random_state=0 - ), - ) - - train_dl, train_categories = dataloader_from_patient_data( - patient_data=[patient_to_data[patient] for patient in train_patients], - categories=categories, - bag_size=bag_size, - batch_size=batch_size, - shuffle=True, - num_workers=num_workers, - transform=train_transform, - ) - del categories # Let's not accidentally reuse the original categories - valid_dl, _ = dataloader_from_patient_data( - patient_data=[patient_to_data[patient] for patient in valid_patients], - bag_size=None, # Use all the patient data for validation - categories=train_categories, - batch_size=1, - shuffle=False, - num_workers=num_workers, - transform=None, - ) - if overlap := set(train_patients) & set(valid_patients): - raise RuntimeError( - f"unreachable: unexpected overlap between training and validation set: {overlap}" - ) - - # Sample one bag to infer the input dimensions of the model - bags, coords, bag_sizes, targets = cast( - tuple[Bags, CoordinatesBatch, BagSizes, EncodedTargets], next(iter(train_dl)) - ) - _, _, dim_feats = bags.shape - - # Weigh classes inversely to their occurrence - category_counts = cast(BagDataset, train_dl.dataset).ground_truths.sum(dim=0) + train_dl: DataLoader, + feature_type: str, + train_categories: Sequence[str], +) -> torch.Tensor: + """ + Computes class weights and checks for category issues. + Logs warnings if there are too few or underpopulated categories. + Returns normalized category weights as a torch.Tensor. + """ + if feature_type == "tile": + category_counts = cast(BagDataset, train_dl.dataset).ground_truths.sum(dim=0) + else: + category_counts = cast( + PatientFeatureDataset, train_dl.dataset + ).ground_truths.sum(dim=0) cat_ratio_reciprocal = category_counts.sum() / category_counts category_weights = cat_ratio_reciprocal / cat_ratio_reciprocal.sum() @@ -325,7 +412,7 @@ def setup_model_for_training( raise ValueError(f"not enough categories to train on: {train_categories}") elif any(category_counts < 16): underpopulated_categories = { - category: count + category: int(count) for category, count in zip(train_categories, category_counts, strict=True) if count < 16 } @@ -333,253 +420,21 @@ def setup_model_for_training( f"Some categories do not have enough samples to meaningfully train a model: {underpopulated_categories}. " "You may want to consider removing these categories; the model will likely overfit on the few samples available." ) + return category_weights - # Train the model - model_info = MODEL_REGISTRY['vit'] - ModelClass = model_info["model_class"] - ''' - categories = ['WT', 'MUT'] - dim_input = 1536 - category_weights =[0.8676, 0.1324] - dim_model = 512 - dim_feedforward=512 - n_heads=8 - n_layers=2 - dropout=0.25 -use_alibi=False - ground_truth_label='isMSIH' - train_patients=['TCGA-AH-6644', 'TCGA-AA-3664', 'TCGA-AZ-4614', 'TCGA-D5-5537', 'TCGA-G4-6628', 'TCGA-EI-6882', 'TCGA-AF-2689', 'TCGA-F4-6805', 'TCGA-AZ-4681', 'TCGA-AG-3882', 'TCGA-A6-6648', 'TCGA-AA-A017', 'TCGA-G4-6303', 'TCGA-EI-6508', 'TCGA-AF-6136', 'TCGA-A6-6654', 'TCGA-AD-6895', 'TCGA-CM-5860', 'TCGA-A6-4105', 'TCGA-F5-6810', 'TCGA-QG-A5YV', 'TCGA-DC-6155', 'TCGA-F5-6812', 'TCGA-QL-A97D', 'TCGA-AZ-4615', 'TCGA-CK-6751', 'TCGA-AH-6547', 'TCGA-AA-3980', 'TCGA-CL-5918', 'TCGA-DM-A282', 'TCGA-AA-3514', 'TCGA-EI-6509', 'TCGA-AF-3911', 'TCGA-CL-5917', 'TCGA-A6-2686', 'TCGA-AA-3950', 'TCGA-AA-3549', 'TCGA-AA-3973', 'TCGA-D5-6922', 'TCGA-AG-3887', 'TCGA-CA-6717', 'TCGA-CM-6171', 'TCGA-AD-6964', 'TCGA-A6-6142', 'TCGA-AD-6890', 'TCGA-A6-6138', 'TCGA-AD-A5EK', 'TCGA-D5-6929', 'TCGA-G4-6588', 'TCGA-AZ-4616', 'TCGA-AA-3561', 'TCGA-QG-A5Z2', 'TCGA-DM-A1HA', 'TCGA-CL-4957', 'TCGA-CA-5255', 'TCGA-DC-6158', 'TCGA-G4-6295', 'TCGA-AY-A69D', 'TCGA-DM-A1D6', 'TCGA-AG-3896', 'TCGA-A6-6652', 'TCGA-CM-6680', 'TCGA-AA-3821', 'TCGA-D5-5540', 'TCGA-DC-5869', 'TCGA-AG-4008', 'TCGA-CK-4952', 'TCGA-AA-3875', 'TCGA-AA-A02W', 'TCGA-EI-6514', 'TCGA-DM-A288', 'TCGA-AG-3890', 'TCGA-AA-3979', 'TCGA-NH-A5IV', 'TCGA-G4-6297', 'TCGA-D5-6927', 'TCGA-AA-3680', 'TCGA-NH-A8F8', 'TCGA-A6-5662', 'TCGA-A6-2674', 'TCGA-AG-A01N', 'TCGA-DM-A1D8', 'TCGA-AA-3837', 'TCGA-AA-A01I', 'TCGA-DM-A0XF', 'TCGA-F4-6855', 'TCGA-CA-6715', 'TCGA-AA-3975', 'TCGA-AA-3989', 'TCGA-AG-A016', 'TCGA-A6-5666', 'TCGA-CK-6746', 'TCGA-CM-4752', 'TCGA-CM-6676', 'TCGA-D5-6541', 'TCGA-A6-5667', 'TCGA-AF-2691', 'TCGA-AD-A5EJ', 'TCGA-F5-6864', 'TCGA-AG-3726', 'TCGA-A6-6649', 'TCGA-AA-A03J', 'TCGA-AF-2687', 'TCGA-AA-3858', 'TCGA-G4-6321', 'TCGA-G5-6641', 'TCGA-G4-6309', 'TCGA-D5-5538', 'TCGA-DM-A28K', 'TCGA-AG-3878', 'TCGA-AZ-6598', 'TCGA-AA-3688', 'TCGA-AG-A01W', 'TCGA-AA-3517', 'TCGA-D5-6898', 'TCGA-CM-5341', 'TCGA-CM-6167', 'TCGA-D5-6531', 'TCGA-DC-6683', 'TCGA-AF-2690', 'TCGA-CM-6161', 'TCGA-A6-5660', 'TCGA-NH-A8F7', 'TCGA-CM-6163', 'TCGA-AA-3968', 'TCGA-CM-5868', 'TCGA-AA-3976', 'TCGA-A6-A565', 'TCGA-AM-5821', 'TCGA-AD-6965', 'TCGA-AY-A71X', 'TCGA-AZ-4682', 'TCGA-AG-3892', 'TCGA-A6-2675', 'TCGA-F4-6569', 'TCGA-AG-3893', 'TCGA-CM-5862', 'TCGA-AZ-6606', 'TCGA-CM-6678', 'TCGA-D5-6931', 'TCGA-D5-6923', 'TCGA-AG-3575', 'TCGA-AA-A00Z', 'TCGA-D5-6540', 'TCGA-A6-3808', 'TCGA-CM-4743', 'TCGA-DC-4749', 'TCGA-T9-A92H', 'TCGA-A6-3810', 'TCGA-AD-6889', 'TCGA-G4-6320', 'TCGA-AA-3971', 'TCGA-AZ-4308', 'TCGA-D5-7000', 'TCGA-DM-A28A', 'TCGA-AG-A01L', 'TCGA-EI-6512', 'TCGA-A6-5661', 'TCGA-AG-3599', 'TCGA-AG-3902', 'TCGA-AD-6548', 'TCGA-AA-A022', 'TCGA-AG-3580', 'TCGA-A6-A567', 'TCGA-AA-3679', 'TCGA-CA-5256', 'TCGA-F4-6808', 'TCGA-D5-6536', 'TCGA-DM-A1D9', 'TCGA-AG-3885', 'TCGA-DM-A1D0', 'TCGA-CM-6674', 'TCGA-EI-6507', 'TCGA-F4-6459', 'TCGA-A6-6137', 'TCGA-AD-6899', 'TCGA-EI-6885', 'TCGA-WS-AB45', 'TCGA-D5-6930', 'TCGA-AU-6004', 'TCGA-AY-6196', 'TCGA-AA-A010', 'TCGA-AG-A00C', 'TCGA-4T-AA8H', 'TCGA-G4-6302', 'TCGA-AA-3966', 'TCGA-AF-2693', 'TCGA-D5-6926', 'TCGA-DM-A0XD', 'TCGA-AA-3854', 'TCGA-D5-6932', 'TCGA-EI-7004', 'TCGA-AG-3594', 'TCGA-EI-6510', 'TCGA-AA-3715', 'TCGA-AA-A01X', 'TCGA-F4-6570', 'TCGA-EI-6511', 'TCGA-CM-5861', 'TCGA-CA-5254', 'TCGA-G4-6317', 'TCGA-DM-A28M', 'TCGA-EI-6506', 'TCGA-AG-A020', 'TCGA-AG-3583', 'TCGA-G4-6294', 'TCGA-CM-6165', 'TCGA-D5-6535', 'TCGA-D5-5541', 'TCGA-DM-A1DB', 'TCGA-AG-A01J', 'TCGA-AG-4001', 'TCGA-AG-A00Y', 'TCGA-AA-3949', 'TCGA-AA-3842', 'TCGA-CA-6716', 'TCGA-CM-5348', 'TCGA-AA-3696', 'TCGA-AA-3833', 'TCGA-NH-A6GC', 'TCGA-CK-4947', 'TCGA-AA-3846', 'TCGA-A6-2677', 'TCGA-A6-2684', 'TCGA-NH-A6GA', 'TCGA-F5-6811', 'TCGA-A6-4107', 'TCGA-F5-6571', 'TCGA-CK-6747', 'TCGA-AA-3666', 'TCGA-CK-5914', 'TCGA-G4-6627', 'TCGA-DC-6681', 'TCGA-AZ-4315', 'TCGA-DT-5265', 'TCGA-CM-4751', 'TCGA-CM-6168', 'TCGA-AY-5543', 'TCGA-AZ-6607', 'TCGA-AG-3901', 'TCGA-AA-3695', 'TCGA-AD-6963', 'TCGA-CK-5913', 'TCGA-AF-2692', 'TCGA-G4-6307', 'TCGA-AA-A01T', 'TCGA-DY-A1DG', 'TCGA-AA-A01C', 'TCGA-AZ-6603', 'TCGA-AG-3727', 'TCGA-AG-3909', 'TCGA-D5-6539', 'TCGA-AZ-5407', 'TCGA-NH-A6GB', 'TCGA-AA-3851', 'TCGA-AF-5654', 'TCGA-AA-3530', 'TCGA-F5-6813', 'TCGA-AA-3845', 'TCGA-AG-3584', 'TCGA-AM-5820', 'TCGA-AA-3982', 'TCGA-D5-6924', 'TCGA-AA-3984', 'TCGA-5M-AATE', 'TCGA-AG-3894', 'TCGA-AU-3779', 'TCGA-A6-2671', 'TCGA-DC-6682', 'TCGA-A6-6653', 'TCGA-DM-A28E', 'TCGA-AZ-5403', 'TCGA-CK-4948', 'TCGA-AF-6655', 'TCGA-AA-A01P', 'TCGA-AA-A024', 'TCGA-F4-6809', 'TCGA-AA-3548', 'TCGA-QG-A5YW', 'TCGA-AA-3818', 'TCGA-EI-6513', 'TCGA-AY-4071', 'TCGA-D5-6532', 'TCGA-AZ-6600', 'TCGA-CM-6172', 'TCGA-AG-A01Y', 'TCGA-AA-3675', 'TCGA-AD-6888', 'TCGA-AH-6897', 'TCGA-AA-3852', 'TCGA-CM-4747', 'TCGA-AG-A02N', 'TCGA-AA-A02F', 'TCGA-DM-A1D7', 'TCGA-AA-3678', 'TCGA-CM-6677', 'TCGA-AA-A004', 'TCGA-D5-5539', 'TCGA-F5-6861', 'TCGA-AA-3544', 'TCGA-F4-6463', 'TCGA-AG-4015', 'TCGA-AF-3400', 'TCGA-CM-6169', 'TCGA-F4-6461', 'TCGA-AD-6901', 'TCGA-DM-A280', 'TCGA-AA-3819', 'TCGA-DM-A0X9', 'TCGA-G4-6315', 'TCGA-AA-3850', 'TCGA-CA-5797', 'TCGA-AA-3856', 'TCGA-AY-6386', 'TCGA-AG-3598', 'TCGA-CM-5863', 'TCGA-AG-3605', 'TCGA-AZ-6599', 'TCGA-CM-5344', 'TCGA-AA-3693', 'TCGA-G4-6306', 'TCGA-F4-6854', 'TCGA-A6-5656', 'TCGA-DY-A0XA', 'TCGA-A6-2685', 'TCGA-AY-6197', 'TCGA-4N-A93T', 'TCGA-NH-A50U', 'TCGA-A6-2683', 'TCGA-AA-3952', 'TCGA-DC-6160', 'TCGA-A6-3807', 'TCGA-AA-3522', 'TCGA-G4-6304', 'TCGA-AA-3526', 'TCGA-DC-4745', 'TCGA-AA-3877', 'TCGA-CM-6162', 'TCGA-AA-3692', 'TCGA-AA-3524', 'TCGA-AG-3881', 'TCGA-AA-3681', 'TCGA-CK-4951', 'TCGA-AA-3534', 'TCGA-A6-A56B', 'TCGA-DM-A1D4', 'TCGA-5M-AAT5', 'TCGA-AG-4022', 'TCGA-AA-3812', 'TCGA-D5-6529', 'TCGA-AA-3673', 'TCGA-EI-6917', 'TCGA-EI-6884', 'TCGA-AG-A011', 'TCGA-AY-A8YK', 'TCGA-EI-6881', 'TCGA-A6-2678', 'TCGA-AA-3947', 'TCGA-A6-5659', 'TCGA-AA-3994', 'TCGA-D5-6534', 'TCGA-AG-3574', 'TCGA-F5-6465', 'TCGA-CA-6719', 'TCGA-AA-3532', 'TCGA-G4-6322', 'TCGA-AF-A56K', 'TCGA-AA-3864', 'TCGA-AA-3685', 'TCGA-AA-3986'] - valid_patients=['TCGA-AG-4021', 'TCGA-AG-A02X', 'TCGA-AA-A02H', 'TCGA-F4-6807', 'TCGA-G4-6311', 'TCGA-AF-A56L', 'TCGA-CM-4748', 'TCGA-AZ-6605', 'TCGA-G4-6299', 'TCGA-AA-3667', 'TCGA-5M-AAT4', 'TCGA-5M-AAT6', 'TCGA-F4-6704', 'TCGA-AH-6544', 'TCGA-3L-AA1B', 'TCGA-CK-4950', 'TCGA-AA-3956', 'TCGA-G4-6323', 'TCGA-AA-3831', 'TCGA-AH-6903', 'TCGA-CA-5796', 'TCGA-AA-3684', 'TCGA-EI-7002', 'TCGA-F4-6460', 'TCGA-AA-3844', 'TCGA-AA-3848', 'TCGA-D5-6928', 'TCGA-A6-5665', 'TCGA-AA-A01S', 'TCGA-DM-A28H', 'TCGA-DC-6157', 'TCGA-DY-A1DD', 'TCGA-AA-3519', 'TCGA-AA-3855', 'TCGA-F4-6806', 'TCGA-AA-3520', 'TCGA-CM-6675', 'TCGA-AG-3898', 'TCGA-CK-6748', 'TCGA-G4-6298', 'TCGA-G4-6626', 'TCGA-QG-A5Z1', 'TCGA-DM-A28G', 'TCGA-A6-2681', 'TCGA-DM-A28F', 'TCGA-G4-6586', 'TCGA-CK-5916', 'TCGA-AG-A002', 'TCGA-AG-A026', 'TCGA-AA-A02E', 'TCGA-F5-6464', 'TCGA-EI-6883', 'TCGA-F4-6856', 'TCGA-AG-A015', 'TCGA-CM-5349', 'TCGA-DM-A1DA', 'TCGA-D5-6530', 'TCGA-A6-A566', 'TCGA-NH-A50V', 'TCGA-F5-6814', 'TCGA-CM-6166', 'TCGA-CI-6622', 'TCGA-AA-3977', 'TCGA-CA-6718', 'TCGA-AA-3841', 'TCGA-AA-3521', 'TCGA-NH-A50T', 'TCGA-AA-A01R', 'TCGA-AG-A008', 'TCGA-CM-4746', 'TCGA-AA-A02R', 'TCGA-DC-6154', 'TCGA-AA-3531', 'TCGA-F5-6863', 'TCGA-F4-6703', 'TCGA-AY-A54L', 'TCGA-AA-3811', 'TCGA-AA-3814', 'TCGA-CK-5912', 'TCGA-A6-3809', 'TCGA-A6-5657', 'TCGA-EF-5830', 'TCGA-CM-6164', 'TCGA-SS-A7HO', 'TCGA-CM-5864', 'TCGA-AF-4110', 'TCGA-AF-6672', 'TCGA-AA-3866', 'TCGA-AA-A01V', 'TCGA-AA-A00N', 'TCGA-D5-6538', 'TCGA-CM-6170', 'TCGA-D5-6537', 'TCGA-AA-A02Y', 'TCGA-DY-A1DC', 'TCGA-AG-3883', 'TCGA-AD-5900', 'TCGA-AA-3955', 'TCGA-AG-3581', 'TCGA-A6-5664', 'TCGA-DY-A1DF', 'TCGA-AA-A02O', 'TCGA-AA-3527', 'TCGA-F5-6702', 'TCGA-AH-6643', 'TCGA-G4-6293', 'TCGA-DC-5337', 'TCGA-AA-A03F', 'TCGA-CI-6624', 'TCGA-AG-3602', 'TCGA-AA-3538', 'TCGA-AA-A01Z', 'TCGA-AA-3558', 'TCGA-AA-3560', 'TCGA-AA-3867', 'TCGA-A6-6650', 'TCGA-CK-5915', 'TCGA-A6-6651', 'TCGA-AY-4070', 'TCGA-AF-A56N', 'TCGA-D5-6533', 'TCGA-AA-3529', 'TCGA-AG-A032', 'TCGA-QG-A5YX'] - - ''' - model = LitVisionTransformer(categories=['WT', 'MUT'], - dim_input=1536, - category_weights=tensor([0.8676, 0.1324]), - dim_model=512, - dim_feedforward=512, - n_heads=8, - n_layers=2, - dropout=0.25, - use_alibi=False, - ground_truth_label='isMSIH', - train_patients=['TCGA-AH-6644', 'TCGA-AA-3664', 'TCGA-AZ-4614', 'TCGA-D5-5537', - 'TCGA-G4-6628', 'TCGA-EI-6882', 'TCGA-AF-2689', 'TCGA-F4-6805', - 'TCGA-AZ-4681', 'TCGA-AG-3882', 'TCGA-A6-6648', 'TCGA-AA-A017', - 'TCGA-G4-6303', 'TCGA-EI-6508', 'TCGA-AF-6136', 'TCGA-A6-6654', - 'TCGA-AD-6895', 'TCGA-CM-5860', 'TCGA-A6-4105', 'TCGA-F5-6810', - 'TCGA-QG-A5YV', 'TCGA-DC-6155', 'TCGA-F5-6812', 'TCGA-QL-A97D', - 'TCGA-AZ-4615', 'TCGA-CK-6751', 'TCGA-AH-6547', 'TCGA-AA-3980', - 'TCGA-CL-5918', 'TCGA-DM-A282', 'TCGA-AA-3514', 'TCGA-EI-6509', - 'TCGA-AF-3911', 'TCGA-CL-5917', 'TCGA-A6-2686', 'TCGA-AA-3950', - 'TCGA-AA-3549', 'TCGA-AA-3973', 'TCGA-D5-6922', 'TCGA-AG-3887', - 'TCGA-CA-6717', 'TCGA-CM-6171', 'TCGA-AD-6964', 'TCGA-A6-6142', - 'TCGA-AD-6890', 'TCGA-A6-6138', 'TCGA-AD-A5EK', 'TCGA-D5-6929', - 'TCGA-G4-6588', 'TCGA-AZ-4616', 'TCGA-AA-3561', 'TCGA-QG-A5Z2', - 'TCGA-DM-A1HA', 'TCGA-CL-4957', 'TCGA-CA-5255', 'TCGA-DC-6158', - 'TCGA-G4-6295', 'TCGA-AY-A69D', 'TCGA-DM-A1D6', 'TCGA-AG-3896', - 'TCGA-A6-6652', 'TCGA-CM-6680', 'TCGA-AA-3821', 'TCGA-D5-5540', - 'TCGA-DC-5869', 'TCGA-AG-4008', 'TCGA-CK-4952', 'TCGA-AA-3875', - 'TCGA-AA-A02W', 'TCGA-EI-6514', 'TCGA-DM-A288', 'TCGA-AG-3890', - 'TCGA-AA-3979', 'TCGA-NH-A5IV', 'TCGA-G4-6297', 'TCGA-D5-6927', - 'TCGA-AA-3680', 'TCGA-NH-A8F8', 'TCGA-A6-5662', 'TCGA-A6-2674', - 'TCGA-AG-A01N', 'TCGA-DM-A1D8', 'TCGA-AA-3837', 'TCGA-AA-A01I', - 'TCGA-DM-A0XF', 'TCGA-F4-6855', 'TCGA-CA-6715', 'TCGA-AA-3975', - 'TCGA-AA-3989', 'TCGA-AG-A016', 'TCGA-A6-5666', 'TCGA-CK-6746', - 'TCGA-CM-4752', 'TCGA-CM-6676', 'TCGA-D5-6541', 'TCGA-A6-5667', - 'TCGA-AF-2691', 'TCGA-AD-A5EJ', 'TCGA-F5-6864', 'TCGA-AG-3726', - 'TCGA-A6-6649', 'TCGA-AA-A03J', 'TCGA-AF-2687', 'TCGA-AA-3858', - 'TCGA-G4-6321', 'TCGA-G5-6641', 'TCGA-G4-6309', 'TCGA-D5-5538', - 'TCGA-DM-A28K', 'TCGA-AG-3878', 'TCGA-AZ-6598', 'TCGA-AA-3688', - 'TCGA-AG-A01W', 'TCGA-AA-3517', 'TCGA-D5-6898', 'TCGA-CM-5341', - 'TCGA-CM-6167', 'TCGA-D5-6531', 'TCGA-DC-6683', 'TCGA-AF-2690', - 'TCGA-CM-6161', 'TCGA-A6-5660', 'TCGA-NH-A8F7', 'TCGA-CM-6163', - 'TCGA-AA-3968', 'TCGA-CM-5868', 'TCGA-AA-3976', 'TCGA-A6-A565', - 'TCGA-AM-5821', 'TCGA-AD-6965', 'TCGA-AY-A71X', 'TCGA-AZ-4682', - 'TCGA-AG-3892', 'TCGA-A6-2675', 'TCGA-F4-6569', 'TCGA-AG-3893', - 'TCGA-CM-5862', 'TCGA-AZ-6606', 'TCGA-CM-6678', 'TCGA-D5-6931', - 'TCGA-D5-6923', 'TCGA-AG-3575', 'TCGA-AA-A00Z', 'TCGA-D5-6540', - 'TCGA-A6-3808', 'TCGA-CM-4743', 'TCGA-DC-4749', 'TCGA-T9-A92H', - 'TCGA-A6-3810', 'TCGA-AD-6889', 'TCGA-G4-6320', 'TCGA-AA-3971', - 'TCGA-AZ-4308', 'TCGA-D5-7000', 'TCGA-DM-A28A', 'TCGA-AG-A01L', - 'TCGA-EI-6512', 'TCGA-A6-5661', 'TCGA-AG-3599', 'TCGA-AG-3902', - 'TCGA-AD-6548', 'TCGA-AA-A022', 'TCGA-AG-3580', 'TCGA-A6-A567', - 'TCGA-AA-3679', 'TCGA-CA-5256', 'TCGA-F4-6808', 'TCGA-D5-6536', - 'TCGA-DM-A1D9', 'TCGA-AG-3885', 'TCGA-DM-A1D0', 'TCGA-CM-6674', - 'TCGA-EI-6507', 'TCGA-F4-6459', 'TCGA-A6-6137', 'TCGA-AD-6899', - 'TCGA-EI-6885', 'TCGA-WS-AB45', 'TCGA-D5-6930', 'TCGA-AU-6004', - 'TCGA-AY-6196', 'TCGA-AA-A010', 'TCGA-AG-A00C', 'TCGA-4T-AA8H', - 'TCGA-G4-6302', 'TCGA-AA-3966', 'TCGA-AF-2693', 'TCGA-D5-6926', - 'TCGA-DM-A0XD', 'TCGA-AA-3854', 'TCGA-D5-6932', 'TCGA-EI-7004', - 'TCGA-AG-3594', 'TCGA-EI-6510', 'TCGA-AA-3715', 'TCGA-AA-A01X', - 'TCGA-F4-6570', 'TCGA-EI-6511', 'TCGA-CM-5861', 'TCGA-CA-5254', - 'TCGA-G4-6317', 'TCGA-DM-A28M', 'TCGA-EI-6506', 'TCGA-AG-A020', - 'TCGA-AG-3583', 'TCGA-G4-6294', 'TCGA-CM-6165', 'TCGA-D5-6535', - 'TCGA-D5-5541', 'TCGA-DM-A1DB', 'TCGA-AG-A01J', 'TCGA-AG-4001', - 'TCGA-AG-A00Y', 'TCGA-AA-3949', 'TCGA-AA-3842', 'TCGA-CA-6716', - 'TCGA-CM-5348', 'TCGA-AA-3696', 'TCGA-AA-3833', 'TCGA-NH-A6GC', - 'TCGA-CK-4947', 'TCGA-AA-3846', 'TCGA-A6-2677', 'TCGA-A6-2684', - 'TCGA-NH-A6GA', 'TCGA-F5-6811', 'TCGA-A6-4107', 'TCGA-F5-6571', - 'TCGA-CK-6747', 'TCGA-AA-3666', 'TCGA-CK-5914', 'TCGA-G4-6627', - 'TCGA-DC-6681', 'TCGA-AZ-4315', 'TCGA-DT-5265', 'TCGA-CM-4751', - 'TCGA-CM-6168', 'TCGA-AY-5543', 'TCGA-AZ-6607', 'TCGA-AG-3901', - 'TCGA-AA-3695', 'TCGA-AD-6963', 'TCGA-CK-5913', 'TCGA-AF-2692', - 'TCGA-G4-6307', 'TCGA-AA-A01T', 'TCGA-DY-A1DG', 'TCGA-AA-A01C', - 'TCGA-AZ-6603', 'TCGA-AG-3727', 'TCGA-AG-3909', 'TCGA-D5-6539', - 'TCGA-AZ-5407', 'TCGA-NH-A6GB', 'TCGA-AA-3851', 'TCGA-AF-5654', - 'TCGA-AA-3530', 'TCGA-F5-6813', 'TCGA-AA-3845', 'TCGA-AG-3584', - 'TCGA-AM-5820', 'TCGA-AA-3982', 'TCGA-D5-6924', 'TCGA-AA-3984', - 'TCGA-5M-AATE', 'TCGA-AG-3894', 'TCGA-AU-3779', 'TCGA-A6-2671', - 'TCGA-DC-6682', 'TCGA-A6-6653', 'TCGA-DM-A28E', 'TCGA-AZ-5403', - 'TCGA-CK-4948', 'TCGA-AF-6655', 'TCGA-AA-A01P', 'TCGA-AA-A024', - 'TCGA-F4-6809', 'TCGA-AA-3548', 'TCGA-QG-A5YW', 'TCGA-AA-3818', - 'TCGA-EI-6513', 'TCGA-AY-4071', 'TCGA-D5-6532', 'TCGA-AZ-6600', - 'TCGA-CM-6172', 'TCGA-AG-A01Y', 'TCGA-AA-3675', 'TCGA-AD-6888', - 'TCGA-AH-6897', 'TCGA-AA-3852', 'TCGA-CM-4747', 'TCGA-AG-A02N', - 'TCGA-AA-A02F', 'TCGA-DM-A1D7', 'TCGA-AA-3678', 'TCGA-CM-6677', - 'TCGA-AA-A004', 'TCGA-D5-5539', 'TCGA-F5-6861', 'TCGA-AA-3544', - 'TCGA-F4-6463', 'TCGA-AG-4015', 'TCGA-AF-3400', 'TCGA-CM-6169', - 'TCGA-F4-6461', 'TCGA-AD-6901', 'TCGA-DM-A280', 'TCGA-AA-3819', - 'TCGA-DM-A0X9', 'TCGA-G4-6315', 'TCGA-AA-3850', 'TCGA-CA-5797', - 'TCGA-AA-3856', 'TCGA-AY-6386', 'TCGA-AG-3598', 'TCGA-CM-5863', - 'TCGA-AG-3605', 'TCGA-AZ-6599', 'TCGA-CM-5344', 'TCGA-AA-3693', - 'TCGA-G4-6306', 'TCGA-F4-6854', 'TCGA-A6-5656', 'TCGA-DY-A0XA', - 'TCGA-A6-2685', 'TCGA-AY-6197', 'TCGA-4N-A93T', 'TCGA-NH-A50U', - 'TCGA-A6-2683', 'TCGA-AA-3952', 'TCGA-DC-6160', 'TCGA-A6-3807', - 'TCGA-AA-3522', 'TCGA-G4-6304', 'TCGA-AA-3526', 'TCGA-DC-4745', - 'TCGA-AA-3877', 'TCGA-CM-6162', 'TCGA-AA-3692', 'TCGA-AA-3524', - 'TCGA-AG-3881', 'TCGA-AA-3681', 'TCGA-CK-4951', 'TCGA-AA-3534', - 'TCGA-A6-A56B', 'TCGA-DM-A1D4', 'TCGA-5M-AAT5', 'TCGA-AG-4022', - 'TCGA-AA-3812', 'TCGA-D5-6529', 'TCGA-AA-3673', 'TCGA-EI-6917', - 'TCGA-EI-6884', 'TCGA-AG-A011', 'TCGA-AY-A8YK', 'TCGA-EI-6881', - 'TCGA-A6-2678', 'TCGA-AA-3947', 'TCGA-A6-5659', 'TCGA-AA-3994', - 'TCGA-D5-6534', 'TCGA-AG-3574', 'TCGA-F5-6465', 'TCGA-CA-6719', - 'TCGA-AA-3532', 'TCGA-G4-6322', 'TCGA-AF-A56K', 'TCGA-AA-3864', - 'TCGA-AA-3685', 'TCGA-AA-3986'], - valid_patients=['TCGA-AG-4021', 'TCGA-AG-A02X', 'TCGA-AA-A02H', 'TCGA-F4-6807', - 'TCGA-G4-6311', 'TCGA-AF-A56L', 'TCGA-CM-4748', 'TCGA-AZ-6605', - 'TCGA-G4-6299', 'TCGA-AA-3667', 'TCGA-5M-AAT4', 'TCGA-5M-AAT6', - 'TCGA-F4-6704', 'TCGA-AH-6544', 'TCGA-3L-AA1B', 'TCGA-CK-4950', - 'TCGA-AA-3956', 'TCGA-G4-6323', 'TCGA-AA-3831', 'TCGA-AH-6903', - 'TCGA-CA-5796', 'TCGA-AA-3684', 'TCGA-EI-7002', 'TCGA-F4-6460', - 'TCGA-AA-3844', 'TCGA-AA-3848', 'TCGA-D5-6928', 'TCGA-A6-5665', - 'TCGA-AA-A01S', 'TCGA-DM-A28H', 'TCGA-DC-6157', 'TCGA-DY-A1DD', - 'TCGA-AA-3519', 'TCGA-AA-3855', 'TCGA-F4-6806', 'TCGA-AA-3520', - 'TCGA-CM-6675', 'TCGA-AG-3898', 'TCGA-CK-6748', 'TCGA-G4-6298', - 'TCGA-G4-6626', 'TCGA-QG-A5Z1', 'TCGA-DM-A28G', 'TCGA-A6-2681', - 'TCGA-DM-A28F', 'TCGA-G4-6586', 'TCGA-CK-5916', 'TCGA-AG-A002', - 'TCGA-AG-A026', 'TCGA-AA-A02E', 'TCGA-F5-6464', 'TCGA-EI-6883', - 'TCGA-F4-6856', 'TCGA-AG-A015', 'TCGA-CM-5349', 'TCGA-DM-A1DA', - 'TCGA-D5-6530', 'TCGA-A6-A566', 'TCGA-NH-A50V', 'TCGA-F5-6814', - 'TCGA-CM-6166', 'TCGA-CI-6622', 'TCGA-AA-3977', 'TCGA-CA-6718', - 'TCGA-AA-3841', 'TCGA-AA-3521', 'TCGA-NH-A50T', 'TCGA-AA-A01R', - 'TCGA-AG-A008', 'TCGA-CM-4746', 'TCGA-AA-A02R', 'TCGA-DC-6154', - 'TCGA-AA-3531', 'TCGA-F5-6863', 'TCGA-F4-6703', 'TCGA-AY-A54L', - 'TCGA-AA-3811', 'TCGA-AA-3814', 'TCGA-CK-5912', 'TCGA-A6-3809', - 'TCGA-A6-5657', 'TCGA-EF-5830', 'TCGA-CM-6164', 'TCGA-SS-A7HO', - 'TCGA-CM-5864', 'TCGA-AF-4110', 'TCGA-AF-6672', 'TCGA-AA-3866', - 'TCGA-AA-A01V', 'TCGA-AA-A00N', 'TCGA-D5-6538', 'TCGA-CM-6170', - 'TCGA-D5-6537', 'TCGA-AA-A02Y', 'TCGA-DY-A1DC', 'TCGA-AG-3883', - 'TCGA-AD-5900', 'TCGA-AA-3955', 'TCGA-AG-3581', 'TCGA-A6-5664', - 'TCGA-DY-A1DF', 'TCGA-AA-A02O', 'TCGA-AA-3527', 'TCGA-F5-6702', - 'TCGA-AH-6643', 'TCGA-G4-6293', 'TCGA-DC-5337', 'TCGA-AA-A03F', - 'TCGA-CI-6624', 'TCGA-AG-3602', 'TCGA-AA-3538', 'TCGA-AA-A01Z', - 'TCGA-AA-3558', 'TCGA-AA-3560', 'TCGA-AA-3867', 'TCGA-A6-6650', - 'TCGA-CK-5915', 'TCGA-A6-6651', 'TCGA-AY-4070', 'TCGA-AF-A56N', - 'TCGA-D5-6533', 'TCGA-AA-3529', 'TCGA-AG-A032', 'TCGA-QG-A5YX'], - ) - - ''' - model = ModelClass( - categories=[np.str_('MUT'), np.str_('WT')], - dim_input = 1536, - category_weights = tensor([0.8676, 0.1324]), - dim_model=512, - dim_feedforward=512, - n_heads=8, - n_layers=2, - dropout=0.25, - - use_alibi=use_alibi, - ground_truth_label=ground_truth_label, - train_patients=train_patients, - valid_patients=valid_patients, - clini_table=clini_table, - slide_table=slide_table, - feature_dir=feature_dir,) - ''' - logger.info( - f"Model 'instantiated with {len(model.state_dict())} parameters." - ) - return model, train_dl, valid_dl - - -def _add_file_handle_(logger: logging.Logger, *, output_dir: Path) -> None: - output_dir.mkdir(exist_ok=True, parents=True) - - file_handler = logging.FileHandler(output_dir / "logfile.log") - file_handler.setLevel(logging.DEBUG) - - formatter = logging.Formatter("%(asctime)s\t%(levelname)s\t%(message)s") - file_handler.setFormatter(formatter) - - logger.addHandler(file_handler) - - -def main(): - '''' - output_dir = os.getenv("TRAINING_OUTPUT_DIR") - print('output_dir:', output_dir) - #_add_file_handle_(_logger, output_dir=Path(output_dir)) - #_logger.info("Using training configuration from environment variables.") - - train_categorical_model_( - output_dir=Path(output_dir), - clini_table=Path(os.getenv("TRAINING_CLINI_TABLE")), - slide_table=Path(os.getenv("TRAINING_SLIDE_TABLE")), - feature_dir=Path(os.getenv("TRAINING_FEATURE_DIR")), - patient_label=os.getenv("TRAINING_PATIENT_LABEL"), - ground_truth_label=os.getenv("TRAINING_GROUND_TRUTH_LABEL"), - filename_label=os.getenv("TRAINING_FILENAME_LABEL"), - categories=os.getenv("TRAINING_CATEGORIES").split(","), - # Dataset and loader parameters - bag_size=int(os.getenv("TRAINING_BAG_SIZE")), - num_workers=int(os.getenv("TRAINING_NUM_WORKERS")), - # Training parameters - batch_size=int(os.getenv("TRAINING_BATCH_SIZE")), - max_epochs=int(os.getenv("TRAINING_MAX_EPOCHS")), - patience=int(os.getenv("TRAINING_PATIENCE")), - accelerator=os.getenv("TRAINING_ACCELERATOR"), - # Experimental features - use_vary_precision_transform=os.getenv("USE_VARY_PRECISION_TRANSFORM", "False").lower() == "true", - use_alibi=os.getenv("USE_ALIBI", "False").lower() == "true", - ) -''' - output_dir = "/mnt/swarm_alpha/ECDP2025/pathology_data/TCGA/TCGA-CRC/STAMP_crossval_new" - #logger.info('output_dir:', output_dir) - # _add_file_handle_(_logger, output_dir=Path(output_dir)) - #_logger.info("Using training configuration from environment variables.") - - train_categorical_model_( - output_dir=Path(output_dir), - clini_table=Path("/mnt/swarm_alpha/ECDP2025/pathology_data/TCGA/TCGA-CRC/TCGA-CRC-DX_clini.xlsx"), - slide_table=Path("/mnt/swarm_alpha/ECDP2025/pathology_data/TCGA/TCGA-CRC/TCGA-CRC-DX_slide_h5.csv"), - feature_dir=Path("/mnt/swarm_alpha/ECDP2025/pathology_data/TCGA/TCGA-CRC/uni2-02627079"), - patient_label="PATIENT", - ground_truth_label="isMSIH", - filename_label="FILENAME", - categories=None, - # Dataset and loader parameters - bag_size=512, - num_workers=15, - # Training parameters - batch_size=64, - max_epochs=64, # 64 - patience=16, - accelerator="gpu", - # Experimental features - use_vary_precision_transform='true', - use_alibi='false', +def log_total_class_summary( + patient_to_data: Mapping[PatientId, PatientData], + categories: Sequence[Category] | None, +) -> None: + ground_truths = [ + patient_data.ground_truth + for patient_data in patient_to_data.values() + if patient_data.ground_truth is not None + ] + cats = categories or sorted(set(ground_truths)) + counter = Counter(ground_truths) + _logger.info( + f"Total samples: {len(ground_truths)} | " + + " | ".join([f"Class {cls}: {counter.get(cls, 0)}" for cls in cats]) ) -if __name__ == "__main__": - main() diff --git a/application/jobs/stamp/app/custom/modeling/config.py b/application/jobs/stamp/app/custom/modeling/config.py new file mode 100644 index 00000000..34917b27 --- /dev/null +++ b/application/jobs/stamp/app/custom/modeling/config.py @@ -0,0 +1,100 @@ +import os +from collections.abc import Sequence +from pathlib import Path + +import torch +from pydantic import BaseModel, ConfigDict, Field + +from .registry import ModelName +from .types import Category, PandasLabel + + +class TrainConfig(BaseModel): + model_config = ConfigDict(extra="forbid") + + output_dir: Path = Field(description="The directory to save the results to") + + clini_table: Path = Field(description="Excel or CSV to read clinical data from") + slide_table: Path | None = Field( + default=None, description="Excel or CSV to read patient-slide associations from" + ) + feature_dir: Path = Field(description="Directory containing feature files") + + ground_truth_label: PandasLabel = Field( + description="Name of categorical column in clinical table to train on" + ) + categories: Sequence[Category] | None = None + + patient_label: PandasLabel = "PATIENT" + filename_label: PandasLabel = "FILENAME" + + params_path: Path | None = Field( + default=None, + description="Optional: Path to a YAML file with advanced training parameters.", + ) + + # Experimental features + use_vary_precision_transform: bool = False + + +class CrossvalConfig(TrainConfig): + n_splits: int = Field(5, ge=2) + + +class DeploymentConfig(BaseModel): + model_config = ConfigDict(extra="forbid") + + output_dir: Path + + checkpoint_paths: list[Path] + clini_table: Path | None = None + slide_table: Path + feature_dir: Path + + ground_truth_label: PandasLabel | None = None + patient_label: PandasLabel = "PATIENT" + filename_label: PandasLabel = "FILENAME" + + num_workers: int = min(os.cpu_count() or 1, 16) + accelerator: str = "gpu" if torch.cuda.is_available() else "cpu" + + +class VitModelParams(BaseModel): + model_config = ConfigDict(extra="forbid") + dim_model: int = 512 + dim_feedforward: int = 512 + n_heads: int = 8 + n_layers: int = 2 + dropout: float = 0.0 + # Experimental feature: Use ALiBi positional embedding + use_alibi: bool = False + + +class MlpModelParams(BaseModel): + model_config = ConfigDict(extra="forbid") + dim_hidden: int = 512 + num_layers: int = 2 + dropout: float = 0.25 + + +class ModelParams(BaseModel): + model_config = ConfigDict(extra="forbid") + vit: VitModelParams + mlp: MlpModelParams + + +class AdvancedConfig(BaseModel): + model_config = ConfigDict(extra="forbid") + bag_size: int = 512 + num_workers: int = min(os.cpu_count() or 1, 16) + batch_size: int = 64 + max_epochs: int = 32 + patience: int = 16 + accelerator: str = "gpu" if torch.cuda.is_available() else "cpu" + max_lr: float = 1e-4 + div_factor: float = 25.0 + model_name: ModelName | None = Field( + default=None, + description='Optional: "vit" or "mlp". Defaults based on feature type.', + ) + model_params: ModelParams diff --git a/docker_config/Dockerfile_stamp b/docker_config/Dockerfile_stamp index 82dd9ed1..b1003e46 100644 --- a/docker_config/Dockerfile_stamp +++ b/docker_config/Dockerfile_stamp @@ -50,4 +50,4 @@ RUN mkdir -p /fl_admin/transfer && ln -s /MediSwarm /fl_admin/transfer/MediSwarm WORKDIR /workspace/ #docker build -f docker_config/Dockerfile_stamp -t stamp-image . -#docker run --rm -it stamp-image:latest-image bash \ No newline at end of file +#docker run --rm -it stamp-image:latest bash \ No newline at end of file