diff --git a/README.md b/README.md
index 4567b272..585172c6 100644
--- a/README.md
+++ b/README.md
@@ -1,278 +1,308 @@
-# Sparse Attention Hub
+
+
+
+
+
+
+
+
+
-A comprehensive framework for sparse attention mechanisms in deep learning models. This project provides implementations of various sparse attention algorithms, benchmarking tools, and seamless integration with popular model frameworks like HuggingFace Transformers.
+
-## ๐ Features
+**A framework for implementing and evaluating sparse attention mechanisms in transformer models**
-- **Multiple Sparse Attention Algorithms**: Implementations of efficient attention mechanisms including Double Sparsity, Hash Attention, and various research-oriented masking strategies
-- **Framework Integration**: Seamless integration with HuggingFace Transformers and other popular frameworks
-- **Comprehensive Benchmarking**: Built-in support for LongBench, Loogle, InfBench, and custom benchmarks
-- **Advanced Metrics**: Micro-metrics logging system for detailed performance analysis
-- **Visualization Tools**: Generate plots and heatmaps for attention pattern analysis
-- **Extensible Architecture**: Modular design for easy extension and customization
+[](https://www.python.org/)
+[](https://pytorch.org/)
+[](https://huggingface.co/transformers/)
+[](LICENSE)
+[](https://pytest.org/)
+[](https://github.com/psf/black)
-## ๐ฆ Installation
+
-### From Source
+Sparse Attention Hub provides efficient implementations of sparse attention algorithms for transformer models. It includes research-oriented attention mechanisms, production-ready implementations, comprehensive benchmarking tools, and seamless integration with HuggingFace Transformers.
+
+## Key Features
+
+- **Multiple Sparse Attention Algorithms**: Hash Attention, Double Sparsity, and configurable masking strategies
+- **HuggingFace Integration**: Drop-in replacement for standard attention with minimal code changes
+- **Comprehensive Benchmarking**: Built-in support for LongBench, Ruler, AIME, InfiniteBench, and custom datasets
+- **Research Tools**: Flexible masker system for experimenting with novel attention patterns
+- **Performance Monitoring**: Detailed metrics logging and visualization capabilities
+- **Modular Architecture**: Easy to extend with custom attention mechanisms
+
+## Installation
+
+### Using Poetry (Recommended)
```bash
+# Install Poetry if you haven't already
+curl -sSL https://install.python-poetry.org | python3 -
+
+# Clone and install the project
git clone https://github.com/xAlg-ai/sparse-attention-hub.git
cd sparse-attention-hub
-pip install -e .
+poetry install
```
-### Dependencies
+### Development Installation
```bash
-pip install -r requirements.txt
-```
+# Install with development dependencies
+poetry install --with dev
-## ๐๏ธ Architecture
+# Install with all optional dependencies
+poetry install --with dev,docs,benchmarks
+```
-The framework is organized into several key modules:
+### From Source (pip)
-### Sparse Attention
-- **Base Classes**: `SparseAttention`, `EfficientAttention`, `ResearchAttention`
-- **Efficient Implementations**: `DoubleSparsity`, `HashAttention`
-- **Research Maskers**: Various masking strategies for attention patterns
-- **Generators**: Integration interfaces for different frameworks
+```bash
+git clone https://github.com/xAlg-ai/sparse-attention-hub.git
+cd sparse-attention-hub
+pip install -e .
+```
-### Adapter System
-- **ModelAdapterHF**: Unified adapter for HuggingFace integration
-- **Request/RequestResponse**: Structured request/response handling
-- **ModelAdapter**: Abstract base class for model adapters
-- **ModelHubAdapterInterface**: Interface for model hosting libraries
-- **SparseAttentionAdapterInterface**: Interface for sparse attention integration
+### Requirements
-### Benchmarking
-- **Benchmark**: Abstract benchmark interface
-- **Datasets**: LongBench, Loogle, InfBench implementations
-- **BenchmarkExecutor**: Execution and result management
-- **ResultStorage**: Persistent storage for benchmark results
+The project requires Python 3.9+ and key dependencies include PyTorch, Transformers, and several benchmarking libraries. All dependencies are managed through Poetry and defined in `pyproject.toml`.
-### Metrics & Visualization
-- **MicroMetricLogger**: Singleton logger for detailed metrics
-- **MicroMetrics**: TopkRecall, LocalError, SampleVariance implementations
-- **PlotGenerator**: Visualization tools with multiple granularity levels
+## Quick Start
-## ๐ Quick Start
+### Basic Usage with HuggingFace Models
```python
-from sparse_attention_hub.adapters import ModelAdapterHF, Request, RequestResponse
-from sparse_attention_hub.sparse_attention.research_attention import ResearchAttentionConfig
+from sparse_attention_hub.adapters import ModelAdapterHF, Request
+from sparse_attention_hub.sparse_attention import ResearchAttentionConfig
from sparse_attention_hub.sparse_attention.research_attention.maskers.fixed.implementations import (
LocalMaskerConfig, SinkMaskerConfig
)
-from sparse_attention_hub.benchmark import BenchmarkExecutor
-from sparse_attention_hub.plotting import PlotGenerator, Granularity
-
-# Create sparse attention configuration
-local_config = LocalMaskerConfig(window_size=16)
-sink_config = SinkMaskerConfig(sink_size=4)
-sparse_attention_config = ResearchAttentionConfig(
- masker_configs=[local_config, sink_config]
-)
+
+# Configure sparse attention (StreamingLLM pattern)
+sparse_config = ResearchAttentionConfig(masker_configs=[
+ SinkMaskerConfig(sink_size=4), # Keep first 4 tokens
+ LocalMaskerConfig(window_size=64) # Keep last 64 tokens
+])
# Create model adapter
adapter = ModelAdapterHF(
model_name="microsoft/DialoGPT-small",
- sparse_attention_config=sparse_attention_config,
+ sparse_attention_config=sparse_config,
device="auto"
)
# Create request
request = Request(
- context="The capital of France is Paris. It is known for the Eiffel Tower.",
- questions=["What is the capital of France?", "What is Paris known for?"]
+ context="Paris is the capital of France. It is famous for the Eiffel Tower and the Louvre Museum.",
+ questions=["What is the capital of France?", "What is Paris famous for?"]
)
-# Process request with sparse attention
+# Process with sparse attention
with adapter.enable_sparse_mode():
response = adapter.process_request(request)
- print(response.responses) # ['Paris', 'The Eiffel Tower']
+ print(response.responses)
-# Process request with dense attention (default)
+# Process with dense attention (default)
response = adapter.process_request(request)
print(response.responses)
-
-# Run benchmarks and visualizations
-benchmark_executor = BenchmarkExecutor()
-plot_generator = PlotGenerator()
-plot_path = plot_generator.generate_plot(Granularity.PER_HEAD)
```
-## ๐ Benchmarking
+### Available Sparse Attention Methods
-The framework supports multiple benchmark datasets and integrates seamlessly with the new adapter system:
+```python
+# Hash Attention
+from sparse_attention_hub.sparse_attention import HashAttentionConfig, EfficientAttentionConfig
+
+hash_config = EfficientAttentionConfig(
+ implementation_config=HashAttentionConfig(
+ num_hash_functions=4,
+ hash_budget=512
+ )
+)
-- **LongBench**: Long-context understanding tasks
-- **Loogle**: Dependency tracking benchmarks
-- **InfBench**: Infinite context benchmarks
+# Double Sparsity
+from sparse_attention_hub.sparse_attention import DoubleSparsityConfig
-```python
-from sparse_attention_hub.benchmark import BenchmarkExecutor
-from sparse_attention_hub.benchmark.datasets import LongBench
-from sparse_attention_hub.adapters import ModelAdapterHF
+double_config = EfficientAttentionConfig(
+ implementation_config=DoubleSparsityConfig(
+ channel_config=ChannelConfig(channels_per_head=32),
+ sparsity_ratio=0.1
+ )
+)
-# Create adapter with sparse attention
-adapter = ModelAdapterHF(
- model_name="microsoft/DialoGPT-small",
- sparse_attention_config=your_sparse_config
+# Custom masking patterns
+from sparse_attention_hub.sparse_attention.research_attention.maskers.fixed.implementations import (
+ TopKMaskerConfig, OracleTopK
)
-# Run benchmarks
-executor = BenchmarkExecutor()
-benchmark = LongBench()
-results = executor.evaluate(benchmark, adapter)
+custom_config = ResearchAttentionConfig(masker_configs=[
+ TopKMaskerConfig(k=128),
+ SinkMaskerConfig(sink_size=4)
+])
```
-## ๐ Metrics and Logging
+## Benchmarking
-Track detailed performance metrics:
+### Running Benchmarks
```python
-from sparse_attention_hub.metrics import MicroMetricLogger
-from sparse_attention_hub.metrics.implementations import TopkRecall
+from benchmark import BenchmarkExecutor, BenchmarkConfig
+from benchmark.executor_config import AdapterConfig
-logger = MicroMetricLogger()
-metric = TopkRecall(k=10)
-logger.register_metric(metric)
-logger.enable_metric_logging(metric)
+# Configure benchmark
+benchmark_config = BenchmarkConfig(
+ benchmark_name="longbench",
+ subsets=["narrativeqa", "qasper"]
+)
+
+adapter_config = AdapterConfig(
+ model_name="microsoft/DialoGPT-small",
+ sparse_attention_config=sparse_config
+)
-# Log metrics during model execution
-logger.log("layer_1", metric, computed_value)
+# Run evaluation
+executor = BenchmarkExecutor(
+ result_dir="./results",
+ gpus=[0, 1],
+ max_concurrent_runs=2
+)
+
+results = executor.run_benchmarks(
+ benchmark_configs=[benchmark_config],
+ adapter_configs=[adapter_config]
+)
```
-## ๐จ Visualization
+### Supported Benchmarks
-Generate attention pattern visualizations:
+- **LongBench**: Long-context understanding tasks (22 standard + 13 extended datasets)
+- **Ruler**: Synthetic tasks for testing context length capabilities
+- **AIME 2024/2025**: Mathematical reasoning benchmarks
+- **InfiniteBench**: Infinite context evaluation tasks
+- **Loogle**: Dependency tracking and retrieval tasks
+- **ZeroScrolls**: Long document understanding
+- **Custom benchmarks**: Easy to add new evaluation datasets
-```python
-from sparse_attention_hub.plotting import PlotGenerator, Granularity
+## Architecture
-generator = PlotGenerator()
+### Core Components
-# Generate different types of plots
-token_plot = generator.generate_plot(Granularity.PER_TOKEN, data)
-head_plot = generator.generate_plot(Granularity.PER_HEAD, data)
-layer_plot = generator.generate_plot(Granularity.PER_LAYER, data)
+- **`sparse_attention/`**: Core sparse attention implementations
+ - `base.py`: Abstract interfaces for all attention mechanisms
+ - `efficient_attention/`: Production-ready algorithms (Hash Attention, Double Sparsity)
+ - `research_attention/`: Experimental masking strategies for research
+ - `utils/`: Common utilities and mask operations
+
+- **`adapters/`**: Integration layer for different frameworks
+ - `huggingface.py`: HuggingFace Transformers integration
+ - `model_servers/`: Centralized model and tokenizer management
+ - `base.py`: Abstract adapter interfaces
+
+- **`benchmark/`**: Comprehensive benchmarking system
+ - Individual benchmark implementations (LongBench, Ruler, etc.)
+ - `executor.py`: Parallel execution with GPU management
+ - `base.py`: Abstract benchmark interface
+
+- **`metric_logging/`**: Performance monitoring and analysis
+- **`plotting/`**: Visualization tools for attention patterns and results
+
+## Advanced Usage
+
+### Custom Maskers
+
+```python
+from sparse_attention_hub.sparse_attention.research_attention.maskers.base import ResearchMasker
+from sparse_attention_hub.sparse_attention.utils import Mask
+
+class CustomMasker(ResearchMasker):
+ def add_mask(self, keys, queries, values, previous_mask, **kwargs):
+ # Implement custom masking logic
+ custom_mask = self.create_custom_mask(queries.shape)
+ return previous_mask.combine_with(custom_mask)
```
-## ๐งช Testing
+### Metrics and Monitoring
+
+```python
+from sparse_attention_hub.metric_logging import MicroMetricLogger
+
+logger = MicroMetricLogger()
+# Metrics are automatically logged when using ResearchAttention
+# Access logged data for analysis
+metrics_data = logger.get_logged_metrics()
+```
-Run the test suite:
+## Testing
```bash
# Run all tests
python scripts/run_tests.py --type all
-# Run only unit tests
+# Run specific test categories
python scripts/run_tests.py --type unit
-
-# Run specific test
-python scripts/run_tests.py --type specific --test-path tests/unit/test_metrics.py
-
-# Discover available tests
-python scripts/run_tests.py --discover
+python scripts/run_tests.py --type integration
# Run tests with coverage
make test-coverage
```
-## ๐ง Development Tools
+## Development
-### Code Formatting and Linting
-
-The project uses comprehensive linting and formatting tools:
+### Code Quality Tools
```bash
# Install development dependencies
-pip install -r requirements-dev.txt
+poetry install --with dev
+
+# Activate Poetry shell
+poetry shell
# Format code
bash scripts/format.sh
-# Run all linting checks
+# Run linting
bash scripts/lint.sh
-# Run specific linters
-bash scripts/lint.sh --flake8
-bash scripts/lint.sh --mypy
-bash scripts/lint.sh --pylint
-bash scripts/lint.sh --bandit
-
-# Using Make commands
-make format # Format code
-make lint # Run all linting
-make dev-check # Quick format + lint check
-```
-
-### Pre-commit Hooks
-
-Set up pre-commit hooks for automatic code quality checks:
-
-```bash
-# Install pre-commit hooks
+# Set up pre-commit hooks
pre-commit install
-
-# Run pre-commit on all files
-pre-commit run --all-files
-
-# Update pre-commit hooks
-pre-commit autoupdate
```
-### Development Workflow
-
-```bash
-# Complete development setup
-make dev-setup
-
-# Run all development checks
-make dev-check
-
-# Simulate CI pipeline
-make ci
-```
-
-## ๐ Project Structure
+### Project Structure
```
sparse-attention-hub/
โโโ sparse_attention_hub/ # Main package
-โ โโโ sparse_attention/ # Sparse attention implementations
-โ โโโ adapters/ # Model adapter system
-โ โโโ benchmark/ # Benchmarking tools
-โ โโโ metrics/ # Metrics and logging
-โ โโโ plotting/ # Visualization tools
-โ โโโ testing/ # Testing utilities
-โโโ tests/ # Test suite
-โ โโโ unit/ # Unit tests
-โ โโโ integration/ # Integration tests
-โโโ scripts/ # Utility scripts
-โโโ tutorials/ # Tutorial notebooks and examples
+โ โโโ sparse_attention/ # Sparse attention implementations
+โ โโโ adapters/ # Model integration adapters
+โ โโโ metric_logging/ # Performance monitoring
+โ โโโ plotting/ # Visualization tools
+โโโ benchmark/ # Benchmarking framework
+โ โโโ longbench/ # LongBench implementation
+โ โโโ ruler/ # Ruler benchmark
+โ โโโ AIME2024/, AIME2025/ # Mathematical reasoning
+โ โโโ scripts/ # Benchmark execution scripts
+โโโ tests/ # Test suite
+โ โโโ unit/ # Unit tests
+โ โโโ integration/ # Integration tests
+โโโ tutorials/ # Examples and tutorials
โโโ docs/ # Documentation
```
-## ๐ค Contributing
-
-We welcome contributions! Please see our contributing guidelines for details on:
+## Contributing
-- Code style and formatting
-- Testing requirements
-- Documentation standards
-- Pull request process
+We welcome contributions to improve sparse attention implementations, add new benchmarks, or enhance the framework. Please ensure all code follows the project's formatting standards and includes appropriate tests.
-## ๐ License
+## License
-This project is licensed under the MIT License - see the LICENSE file for details.
+This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
-## ๐ Links
+## Links
- **Repository**: https://github.com/xAlg-ai/sparse-attention-hub
- **Documentation**: https://sparse-attention-hub.readthedocs.io
- **Issues**: https://github.com/xAlg-ai/sparse-attention-hub/issues
-## ๐ Acknowledgments
+## Acknowledgments
-This project implements and extends various sparse attention mechanisms from the research community. We acknowledge the original authors of these algorithms and the open-source community for their contributions.
\ No newline at end of file
+This project implements sparse attention mechanisms from various research papers. We acknowledge the original authors and the open-source community for their contributions to advancing efficient attention algorithms.
\ No newline at end of file
diff --git a/benchmark/AIME2024/__init__.py b/benchmark/AIME2024/__init__.py
index bb567237..58102847 100644
--- a/benchmark/AIME2024/__init__.py
+++ b/benchmark/AIME2024/__init__.py
@@ -8,4 +8,4 @@
from .calculate_metrics import calculate_metrics
from .aime2024 import AIME2024
-__all__ = ["calculate_metrics", "AIME2024"]
\ No newline at end of file
+__all__ = ["calculate_metrics", "AIME2024"]
diff --git a/benchmark/AIME2024/aime2024.py b/benchmark/AIME2024/aime2024.py
index 8ffc779c..c068813c 100644
--- a/benchmark/AIME2024/aime2024.py
+++ b/benchmark/AIME2024/aime2024.py
@@ -29,22 +29,23 @@ class AIME2024(Benchmark):
# AIME2024 has a single dataset
all_datasets: List[str] = ["aime2024"]
-
+
benchmark_name: str = "aime2024"
huggingface_dataset_id: str = "xAlg-AI/att-hub-aime2024"
def _load_datasets(self) -> pd.DataFrame:
"""Load AIME2024 dataset.
-
+
AIME2024 uses a single dataset with all problems.
-
+
Returns:
pandas DataFrame with all AIME2024 problems.
"""
print(f"Loading AIME2024 dataset")
-
+
try:
from datasets import load_dataset
+
dataset = load_dataset(self.huggingface_dataset_id, split="test")
df = dataset.to_pandas()
df["task"] = "aime2024" # Ensure task column exists
@@ -72,7 +73,7 @@ def post_run_evaluate(self, results_df: pd.DataFrame) -> Dict[str, Any]:
# Use the calculate_metrics function from HashAttention evaluation
metrics: Dict[str, Any] = calculate_metrics(results_df)
-
+
# Format the results for consistency with other benchmarks
overall_metrics: Dict[str, Any] = {
"overall_score": round(metrics["accuracy"], 4),
@@ -84,16 +85,19 @@ def post_run_evaluate(self, results_df: pd.DataFrame) -> Dict[str, Any]:
"task_scores": {
"aime2024": {
"accuracy": round(metrics["accuracy"], 4),
- "extraction_success_rate": round(metrics["extraction_success_rate"], 4)
+ "extraction_success_rate": round(
+ metrics["extraction_success_rate"], 4
+ ),
}
},
- "summary": {
- "total_tasks": 1,
- "total_samples": len(results_df)
- }
+ "summary": {"total_tasks": 1, "total_samples": len(results_df)},
}
-
- print(f" โ AIME2024 Accuracy: {metrics['accuracy']:.3f} ({metrics['accuracy']*100:.1f}%)")
- print(f" โ Extraction Success Rate: {metrics['extraction_success_rate']:.3f} ({metrics['extraction_success_rate']*100:.1f}%)")
-
- return overall_metrics
\ No newline at end of file
+
+ print(
+ f" โ AIME2024 Accuracy: {metrics['accuracy']:.3f} ({metrics['accuracy']*100:.1f}%)"
+ )
+ print(
+ f" โ Extraction Success Rate: {metrics['extraction_success_rate']:.3f} ({metrics['extraction_success_rate']*100:.1f}%)"
+ )
+
+ return overall_metrics
diff --git a/benchmark/AIME2024/calculate_metrics.py b/benchmark/AIME2024/calculate_metrics.py
index 6f9270cf..7f882a6c 100644
--- a/benchmark/AIME2024/calculate_metrics.py
+++ b/benchmark/AIME2024/calculate_metrics.py
@@ -5,51 +5,53 @@
import pandas as pd
from typing import List, Dict, Any
+
def extract_boxed_answer(text: str) -> str:
"""
Extract the answer from \boxed{...} format in the text.
-
+
Args:
text: The model's response text
-
+
Returns:
The extracted answer as a string, or empty string if not found
"""
# Look for \boxed{...} pattern
- boxed_pattern = r'\\boxed\{([^}]*)\}'
+ boxed_pattern = r"\\boxed\{([^}]*)\}"
matches = re.findall(boxed_pattern, text)
-
+
if matches:
# Take the last boxed answer in case there are multiple
answer = matches[-1].strip()
-
+
# Extract just the number if there's additional formatting
# Handle cases like "033", "23", "$23$", etc.
- number_match = re.search(r'\d+', answer)
+ number_match = re.search(r"\d+", answer)
if number_match:
return number_match.group()
else:
return answer
-
+
# Fallback: look for numbers at the end of the text
# This handles cases where the model doesn't use \boxed format
- lines = text.strip().split('\n')
+ lines = text.strip().split("\n")
for line in reversed(lines):
if line.strip():
# Look for a number in the last non-empty line
- number_match = re.search(r'\b(\d{1,3})\b', line)
+ number_match = re.search(r"\b(\d{1,3})\b", line)
if number_match:
return number_match.group(1)
-
+
return ""
+
def normalize_answer(answer: str) -> str:
"""
Normalize an answer to a standard format.
-
+
Args:
answer: The answer string to normalize
-
+
Returns:
Normalized answer string
"""
@@ -59,34 +61,37 @@ def normalize_answer(answer: str) -> str:
return str(int(answer))
return answer
+
def calculate_metrics(df: pd.DataFrame) -> Dict[str, Any]:
"""
Calculate evaluation metrics for AIME2024 benchmark.
-
+
Args:
df: DataFrame with columns 'answer' (ground truth) and 'predicted_answer' (model output)
-
+
Returns:
Dictionary containing evaluation metrics
"""
- if 'predicted_answer' not in df.columns:
+ if "predicted_answer" not in df.columns:
raise ValueError("DataFrame must contain 'predicted_answer' column")
- if 'answer' not in df.columns:
+ if "answer" not in df.columns:
raise ValueError("DataFrame must contain 'answer' column")
-
+
total_problems = len(df)
correct_answers = 0
extraction_failures = 0
-
+
detailed_results = []
-
+
for idx, row in df.iterrows():
- ground_truth = normalize_answer(str(row['answer']))
- predicted_text = str(row['predicted_answer']) if pd.notna(row['predicted_answer']) else ""
-
+ ground_truth = normalize_answer(str(row["answer"]))
+ predicted_text = (
+ str(row["predicted_answer"]) if pd.notna(row["predicted_answer"]) else ""
+ )
+
# Extract the predicted answer
extracted_answer = extract_boxed_answer(predicted_text)
-
+
if not extracted_answer:
extraction_failures += 1
is_correct = False
@@ -95,35 +100,42 @@ def calculate_metrics(df: pd.DataFrame) -> Dict[str, Any]:
is_correct = extracted_answer == ground_truth
if is_correct:
correct_answers += 1
-
- detailed_results.append({
- 'id': row.get('id', f'problem_{idx}'),
- 'ground_truth': ground_truth,
- 'predicted_text': predicted_text,
- 'extracted_answer': extracted_answer,
- 'is_correct': is_correct,
- 'extraction_failed': not bool(extracted_answer)
- })
-
+
+ detailed_results.append(
+ {
+ "id": row.get("id", f"problem_{idx}"),
+ "ground_truth": ground_truth,
+ "predicted_text": predicted_text,
+ "extracted_answer": extracted_answer,
+ "is_correct": is_correct,
+ "extraction_failed": not bool(extracted_answer),
+ }
+ )
+
# Calculate metrics
accuracy = correct_answers / total_problems if total_problems > 0 else 0.0
- extraction_success_rate = (total_problems - extraction_failures) / total_problems if total_problems > 0 else 0.0
-
+ extraction_success_rate = (
+ (total_problems - extraction_failures) / total_problems
+ if total_problems > 0
+ else 0.0
+ )
+
metrics = {
- 'accuracy': accuracy,
- 'correct_answers': correct_answers,
- 'total_problems': total_problems,
- 'extraction_success_rate': extraction_success_rate,
- 'extraction_failures': extraction_failures,
- 'detailed_results': detailed_results
+ "accuracy": accuracy,
+ "correct_answers": correct_answers,
+ "total_problems": total_problems,
+ "extraction_success_rate": extraction_success_rate,
+ "extraction_failures": extraction_failures,
+ "detailed_results": detailed_results,
}
-
+
return metrics
+
def print_metrics_summary(metrics: Dict[str, Any]) -> None:
"""
Print a formatted summary of the evaluation metrics.
-
+
Args:
metrics: Dictionary containing evaluation metrics
"""
@@ -132,34 +144,39 @@ def print_metrics_summary(metrics: Dict[str, Any]) -> None:
print(f"Total Problems: {metrics['total_problems']}")
print(f"Correct Answers: {metrics['correct_answers']}")
print(f"Accuracy: {metrics['accuracy']:.3f} ({metrics['accuracy']*100:.1f}%)")
- print(f"Extraction Success Rate: {metrics['extraction_success_rate']:.3f} ({metrics['extraction_success_rate']*100:.1f}%)")
+ print(
+ f"Extraction Success Rate: {metrics['extraction_success_rate']:.3f} ({metrics['extraction_success_rate']*100:.1f}%)"
+ )
print(f"Extraction Failures: {metrics['extraction_failures']}")
-
- if metrics['extraction_failures'] > 0:
- print(f"\nNote: {metrics['extraction_failures']} problems had answer extraction failures.")
+
+ if metrics["extraction_failures"] > 0:
+ print(
+ f"\nNote: {metrics['extraction_failures']} problems had answer extraction failures."
+ )
print("These are counted as incorrect answers.")
+
if __name__ == "__main__":
# Test the metrics calculation
test_data = {
- 'answer': ['23', '33', '156', '902'],
- 'predicted_answer': [
- 'The answer is \\boxed{23}.',
- 'After solving, we get \\boxed{033}.',
- 'Therefore, the answer is \\boxed{156}.',
- 'The final answer is 902.' # Test fallback extraction
+ "answer": ["23", "33", "156", "902"],
+ "predicted_answer": [
+ "The answer is \\boxed{23}.",
+ "After solving, we get \\boxed{033}.",
+ "Therefore, the answer is \\boxed{156}.",
+ "The final answer is 902.", # Test fallback extraction
],
- 'id': ['2024-I-1', '2024-I-2', '2024-I-3', '2024-I-4']
+ "id": ["2024-I-1", "2024-I-2", "2024-I-3", "2024-I-4"],
}
-
+
test_df = pd.DataFrame(test_data)
metrics = calculate_metrics(test_df)
print_metrics_summary(metrics)
-
+
print("\nDetailed Results:")
- for result in metrics['detailed_results']:
+ for result in metrics["detailed_results"]:
print(f"ID: {result['id']}")
print(f" Ground Truth: {result['ground_truth']}")
print(f" Extracted: {result['extracted_answer']}")
print(f" Correct: {result['is_correct']}")
- print()
\ No newline at end of file
+ print()
diff --git a/benchmark/AIME2024/create_huggingface_dataset.py b/benchmark/AIME2024/create_huggingface_dataset.py
index 0a419581..5f470c23 100644
--- a/benchmark/AIME2024/create_huggingface_dataset.py
+++ b/benchmark/AIME2024/create_huggingface_dataset.py
@@ -21,6 +21,7 @@
which is standard in mathematical competition contexts.
"""
+
def create_aime2024_dataset():
"""
Process the AIME2024 dataset and convert it to the standardized benchmark format.
@@ -28,10 +29,10 @@ def create_aime2024_dataset():
# Load the original dataset
dataset = load_dataset("Maxwell-Jia/AIME_2024")
df = dataset["train"].to_pandas()
-
+
# Create the standardized format
processed_data = []
-
+
for _, row in df.iterrows():
# Format the problem with clear instructions about the boxed answer format
context = f"""Solve the following AIME (American Invitational Mathematics Examination) problem.
@@ -41,26 +42,29 @@ def create_aime2024_dataset():
Instructions:
- The answer should be an integer between 0 and 999
- Please reason step by step, and put your final answer within \\boxed{{...}} format"""
-
+
question = "What is the answer to this problem?"
-
+
# The answer prefix encourages the model to show work before the final answer
answer_prefix = ""
-
- processed_data.append({
- 'context': context,
- 'question': question,
- 'answer_prefix': answer_prefix,
- 'answer': str(row['Answer']), # Convert to string for consistency
- 'id': row['ID'],
- 'max_new_tokens': 32000, # Allow comprehensive step-by-step solutions
- })
-
+
+ processed_data.append(
+ {
+ "context": context,
+ "question": question,
+ "answer_prefix": answer_prefix,
+ "answer": str(row["Answer"]), # Convert to string for consistency
+ "id": row["ID"],
+ "max_new_tokens": 32000, # Allow comprehensive step-by-step solutions
+ }
+ )
+
# Convert to Dataset
processed_dataset = Dataset.from_pandas(pd.DataFrame(processed_data))
-
+
return processed_dataset
+
if __name__ == "__main__":
# Test the dataset creation
processed_dataset = create_aime2024_dataset()
@@ -68,4 +72,6 @@ def create_aime2024_dataset():
print("\nFirst example:")
print(processed_dataset[0])
- processed_dataset.push_to_hub("xAlg-AI/att-hub-aime2024", config_name=f"aime2024", split="test")
+ processed_dataset.push_to_hub(
+ "xAlg-AI/att-hub-aime2024", config_name=f"aime2024", split="test"
+ )
diff --git a/benchmark/AIME2024/test_aime2024.py b/benchmark/AIME2024/test_aime2024.py
index 5e42c3fe..90c77ee6 100644
--- a/benchmark/AIME2024/test_aime2024.py
+++ b/benchmark/AIME2024/test_aime2024.py
@@ -11,29 +11,38 @@
import sys
import os
+
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
from create_huggingface_dataset import create_aime2024_dataset
from calculate_metrics import calculate_metrics, print_metrics_summary
import pandas as pd
+
def test_dataset_creation():
"""Test that the dataset can be created successfully."""
print("Testing AIME2024 dataset creation...")
-
+
dataset = create_aime2024_dataset()
-
+
print(f"โ Dataset created successfully with {len(dataset)} problems")
-
+
# Verify dataset structure
- required_keys = ['context', 'question', 'answer_prefix', 'answer', 'id', 'max_new_tokens']
+ required_keys = [
+ "context",
+ "question",
+ "answer_prefix",
+ "answer",
+ "id",
+ "max_new_tokens",
+ ]
first_example = dataset[0]
-
+
for key in required_keys:
assert key in first_example, f"Missing required key: {key}"
-
+
print("โ Dataset structure is correct")
-
+
# Show some examples
print(f"\nSample problems:")
for i in range(min(3, len(dataset))):
@@ -42,99 +51,114 @@ def test_dataset_creation():
print(f" Answer: {example['answer']}")
print(f" Problem preview: {example['context'][:100]}...")
print()
-
+
return dataset
+
def test_metrics_calculation():
"""Test the metrics calculation with sample data."""
print("Testing AIME2024 metrics calculation...")
-
+
# Create test data with various scenarios
test_data = {
- 'answer': ['23', '33', '156', '902', '45'],
- 'predicted_answer': [
- 'The answer is \\boxed{23}.', # Correct with boxed format
- 'After solving, we get \\boxed{033}.', # Correct with leading zeros
- 'Therefore, the answer is \\boxed{156}.', # Correct
- 'The final answer is 902.', # Correct without boxed format
- 'I think the answer is \\boxed{44}.' # Incorrect
+ "answer": ["23", "33", "156", "902", "45"],
+ "predicted_answer": [
+ "The answer is \\boxed{23}.", # Correct with boxed format
+ "After solving, we get \\boxed{033}.", # Correct with leading zeros
+ "Therefore, the answer is \\boxed{156}.", # Correct
+ "The final answer is 902.", # Correct without boxed format
+ "I think the answer is \\boxed{44}.", # Incorrect
],
- 'id': ['2024-I-1', '2024-I-2', '2024-I-3', '2024-I-4', '2024-I-5']
+ "id": ["2024-I-1", "2024-I-2", "2024-I-3", "2024-I-4", "2024-I-5"],
}
-
+
test_df = pd.DataFrame(test_data)
metrics = calculate_metrics(test_df)
-
+
print_metrics_summary(metrics)
-
+
# Verify expected results
- assert metrics['total_problems'] == 5, f"Expected 5 problems, got {metrics['total_problems']}"
- assert metrics['correct_answers'] == 4, f"Expected 4 correct answers, got {metrics['correct_answers']}"
- assert metrics['accuracy'] == 0.8, f"Expected accuracy 0.8, got {metrics['accuracy']}"
- assert metrics['extraction_failures'] == 0, f"Expected 0 extraction failures, got {metrics['extraction_failures']}"
-
+ assert (
+ metrics["total_problems"] == 5
+ ), f"Expected 5 problems, got {metrics['total_problems']}"
+ assert (
+ metrics["correct_answers"] == 4
+ ), f"Expected 4 correct answers, got {metrics['correct_answers']}"
+ assert (
+ metrics["accuracy"] == 0.8
+ ), f"Expected accuracy 0.8, got {metrics['accuracy']}"
+ assert (
+ metrics["extraction_failures"] == 0
+ ), f"Expected 0 extraction failures, got {metrics['extraction_failures']}"
+
print("โ Metrics calculation is correct")
-
+
return metrics
+
def test_integration():
"""Test integration with the main evaluation system."""
print("Testing AIME2024 integration...")
-
+
# Test that the dataset can be imported and used
try:
- sys.path.append('..')
+ sys.path.append("..")
from AIME2024.calculate_metrics import calculate_metrics
from AIME2024.create_huggingface_dataset import create_aime2024_dataset
-
+
# Test dataset creation through import
dataset = create_aime2024_dataset()
print(f"โ Integration test passed - dataset has {len(dataset)} examples")
-
+
# Verify the dataset can be converted to pandas
df = dataset.to_pandas()
print(f"โ Dataset conversion to pandas successful - shape: {df.shape}")
-
+
return True
-
+
except Exception as e:
print(f"โ Integration test failed: {e}")
return False
+
def main():
"""Run all tests for AIME2024 benchmark."""
print("AIME2024 Benchmark Test Suite")
print("=" * 50)
-
+
try:
# Test dataset creation
dataset = test_dataset_creation()
print()
-
+
# Test metrics calculation
metrics = test_metrics_calculation()
print()
-
+
# Test integration
integration_success = test_integration()
print()
-
+
if integration_success:
print("๐ All tests passed! AIME2024 benchmark is ready to use.")
print()
print("Usage examples:")
print(" cd evaluation")
- print(" python evaluate.py --dataset aime2024 --base_model meta-llama/Llama-3.1-8B-Instruct --device cpu")
+ print(
+ " python evaluate.py --dataset aime2024 --base_model meta-llama/Llama-3.1-8B-Instruct --device cpu"
+ )
print(" python evaluate.py --dataset aime2024 --device 1 --num_samples 10")
else:
print("โ Some tests failed. Please check the implementation.")
sys.exit(1)
-
+
except Exception as e:
print(f"โ Test suite failed with error: {e}")
import traceback
+
traceback.print_exc()
sys.exit(1)
+
if __name__ == "__main__":
- main()
\ No newline at end of file
+ main()
diff --git a/benchmark/AIME2025/__init__.py b/benchmark/AIME2025/__init__.py
index b5a69b8d..01b7ec84 100644
--- a/benchmark/AIME2025/__init__.py
+++ b/benchmark/AIME2025/__init__.py
@@ -8,4 +8,4 @@
from .calculate_metrics import calculate_metrics
from .aime2025 import AIME2025
-__all__ = ["calculate_metrics", "AIME2025"]
\ No newline at end of file
+__all__ = ["calculate_metrics", "AIME2025"]
diff --git a/benchmark/AIME2025/aime2025.py b/benchmark/AIME2025/aime2025.py
index 5eebe349..02101668 100644
--- a/benchmark/AIME2025/aime2025.py
+++ b/benchmark/AIME2025/aime2025.py
@@ -29,22 +29,23 @@ class AIME2025(Benchmark):
# AIME2025 has a single dataset
all_datasets: List[str] = ["aime2025"]
-
+
benchmark_name: str = "aime2025"
huggingface_dataset_id: str = "xAlg-AI/att-hub-aime2025"
def _load_datasets(self) -> pd.DataFrame:
"""Load AIME2025 dataset.
-
+
AIME2025 uses a single dataset with all problems.
-
+
Returns:
pandas DataFrame with all AIME2025 problems.
"""
print(f"Loading AIME2025 dataset")
-
+
try:
from datasets import load_dataset
+
dataset = load_dataset(self.huggingface_dataset_id, split="test")
df = dataset.to_pandas()
df["task"] = "aime2025" # Ensure task column exists
@@ -72,7 +73,7 @@ def post_run_evaluate(self, results_df: pd.DataFrame) -> Dict[str, Any]:
# Use the calculate_metrics function from HashAttention evaluation
metrics: Dict[str, Any] = calculate_metrics(results_df)
-
+
# Format the results for consistency with other benchmarks
overall_metrics: Dict[str, Any] = {
"overall_score": round(metrics["exact_match"], 4),
@@ -84,17 +85,20 @@ def post_run_evaluate(self, results_df: pd.DataFrame) -> Dict[str, Any]:
"aime2025": {
"exact_match": round(metrics["exact_match"], 4),
"extraction_rate": round(metrics["extraction_rate"], 4),
- "boxed_format_rate": round(metrics["boxed_format_rate"], 4)
+ "boxed_format_rate": round(metrics["boxed_format_rate"], 4),
}
},
- "summary": {
- "total_tasks": 1,
- "total_samples": len(results_df)
- }
+ "summary": {"total_tasks": 1, "total_samples": len(results_df)},
}
-
- print(f" โ AIME2025 Exact Match: {metrics['exact_match']:.3f} ({metrics['exact_match']*100:.1f}%)")
- print(f" โ Extraction Rate: {metrics['extraction_rate']:.3f} ({metrics['extraction_rate']*100:.1f}%)")
- print(f" โ Boxed Format Rate: {metrics['boxed_format_rate']:.3f} ({metrics['boxed_format_rate']*100:.1f}%)")
-
- return overall_metrics
\ No newline at end of file
+
+ print(
+ f" โ AIME2025 Exact Match: {metrics['exact_match']:.3f} ({metrics['exact_match']*100:.1f}%)"
+ )
+ print(
+ f" โ Extraction Rate: {metrics['extraction_rate']:.3f} ({metrics['extraction_rate']*100:.1f}%)"
+ )
+ print(
+ f" โ Boxed Format Rate: {metrics['boxed_format_rate']:.3f} ({metrics['boxed_format_rate']*100:.1f}%)"
+ )
+
+ return overall_metrics
diff --git a/benchmark/AIME2025/calculate_metrics.py b/benchmark/AIME2025/calculate_metrics.py
index 8dcb6217..0c0b1fbd 100644
--- a/benchmark/AIME2025/calculate_metrics.py
+++ b/benchmark/AIME2025/calculate_metrics.py
@@ -10,38 +10,38 @@
def extract_boxed_answer(text: str) -> str:
"""
Extract the answer from \boxed{...} format in the text.
-
+
Args:
text: The model's response text
-
+
Returns:
The extracted answer as a string, or empty string if not found
"""
# Look for \boxed{...} pattern
- boxed_pattern = r'\\boxed\{([^}]*)\}'
+ boxed_pattern = r"\\boxed\{([^}]*)\}"
matches = re.findall(boxed_pattern, text)
-
+
if matches:
# Return the last boxed answer found (in case there are multiple)
return matches[-1].strip()
-
+
# Fallback: look for boxed without backslash (in case the model omits it)
- boxed_pattern_alt = r'boxed\{([^}]*)\}'
+ boxed_pattern_alt = r"boxed\{([^}]*)\}"
matches = re.findall(boxed_pattern_alt, text)
-
+
if matches:
return matches[-1].strip()
-
+
return ""
def extract_numerical_answer(text: str) -> str:
"""
Extract a numerical answer from text, with fallback strategies.
-
+
Args:
text: The text to extract from
-
+
Returns:
The extracted numerical answer as a string
"""
@@ -49,17 +49,17 @@ def extract_numerical_answer(text: str) -> str:
boxed_answer = extract_boxed_answer(text)
if boxed_answer:
# Extract just the number from the boxed content
- numbers = re.findall(r'\d+', boxed_answer)
+ numbers = re.findall(r"\d+", boxed_answer)
if numbers:
return numbers[-1] # Take the last number found
-
+
# Fallback 1: Look for "answer is X" or "answer: X" patterns
answer_patterns = [
- r'(?:answer|solution)\s*(?:is|:)\s*(\d+)',
- r'(?:the\s+)?answer\s*(?:is|:)\s*(\d+)',
- r'(?:therefore|thus|so)\s*(?:the\s+)?(?:answer|solution)\s*(?:is|:)\s*(\d+)',
+ r"(?:answer|solution)\s*(?:is|:)\s*(\d+)",
+ r"(?:the\s+)?answer\s*(?:is|:)\s*(\d+)",
+ r"(?:therefore|thus|so)\s*(?:the\s+)?(?:answer|solution)\s*(?:is|:)\s*(\d+)",
]
-
+
for pattern in answer_patterns:
matches = re.findall(pattern, text, re.IGNORECASE)
if matches:
@@ -67,125 +67,127 @@ def extract_numerical_answer(text: str) -> str:
num = int(matches[-1])
if 0 <= num <= 999:
return matches[-1]
-
+
# Fallback 2: Look for numbers at the end of the text
# This catches cases where the model just states the number
- lines = text.strip().split('\n')
+ lines = text.strip().split("\n")
for line in reversed(lines):
line = line.strip()
if line:
- numbers = re.findall(r'\b\d+\b', line)
+ numbers = re.findall(r"\b\d+\b", line)
if numbers:
# Check if the number is in valid AIME range (0-999)
num = int(numbers[-1])
if 0 <= num <= 999:
return str(num)
-
+
# Fallback 3: Find any number in valid AIME range
- all_numbers = re.findall(r'\b\d+\b', text)
+ all_numbers = re.findall(r"\b\d+\b", text)
for num_str in reversed(all_numbers): # Check from end to beginning
num = int(num_str)
if 0 <= num <= 999:
return str(num)
-
+
return ""
-def calculate_exact_match_score(predictions: List[str], references: List[List[str]]) -> float:
+def calculate_exact_match_score(
+ predictions: List[str], references: List[List[str]]
+) -> float:
"""
Calculate exact match accuracy between predictions and references.
-
+
Args:
predictions: List of predicted answers
references: List of reference answers (each can have multiple valid answers)
-
+
Returns:
Exact match accuracy as a float between 0 and 1
"""
correct = 0
total = len(predictions)
-
+
for pred, ref_list in zip(predictions, references):
# Extract numerical answer from prediction
pred_answer = extract_numerical_answer(pred)
-
+
# Check if prediction matches any of the reference answers
if pred_answer in ref_list:
correct += 1
-
+
return correct / total if total > 0 else 0.0
def calculate_metrics(df: pd.DataFrame) -> dict:
"""
Calculate metrics for the AIME2025 benchmark.
-
+
Args:
df: DataFrame with columns 'predicted_answer' and 'answer'
-
+
Returns:
Dictionary containing the calculated metrics
"""
predictions = df["predicted_answer"].tolist()
references = df["answer"].tolist()
-
+
# Ensure references are in the correct format (list of lists)
if references and not isinstance(references[0], list):
references = [[str(ref)] for ref in references]
-
+
# Calculate exact match accuracy
exact_match = calculate_exact_match_score(predictions, references)
-
+
# Additional analysis: count how many answers were successfully extracted
extracted_count = 0
boxed_count = 0
-
+
for pred in predictions:
extracted = extract_numerical_answer(pred)
if extracted:
extracted_count += 1
-
+
# Count how many used the boxed format
if extract_boxed_answer(pred):
boxed_count += 1
-
+
extraction_rate = extracted_count / len(predictions) if predictions else 0.0
boxed_format_rate = boxed_count / len(predictions) if predictions else 0.0
-
+
return {
"exact_match": exact_match,
"extraction_rate": extraction_rate,
"boxed_format_rate": boxed_format_rate,
- "total_problems": len(predictions)
+ "total_problems": len(predictions),
}
def analyze_errors(df: pd.DataFrame) -> dict:
"""
Analyze common error patterns in the predictions.
-
+
Args:
df: DataFrame with predictions and references
-
+
Returns:
Dictionary with error analysis
"""
predictions = df["predicted_answer"].tolist()
references = df["answer"].tolist()
-
+
if references and not isinstance(references[0], list):
references = [[str(ref)] for ref in references]
-
+
error_types = {
"no_answer_extracted": 0,
"wrong_answer": 0,
"out_of_range": 0,
- "format_issues": 0
+ "format_issues": 0,
}
-
+
for pred, ref_list in zip(predictions, references):
extracted = extract_numerical_answer(pred)
-
+
if not extracted:
error_types["no_answer_extracted"] += 1
elif extracted not in ref_list:
@@ -196,5 +198,5 @@ def analyze_errors(df: pd.DataFrame) -> dict:
error_types["out_of_range"] += 1
except ValueError:
error_types["format_issues"] += 1
-
- return error_types
\ No newline at end of file
+
+ return error_types
diff --git a/benchmark/AIME2025/create_huggingface_dataset.py b/benchmark/AIME2025/create_huggingface_dataset.py
index b71d7d73..27a78fad 100644
--- a/benchmark/AIME2025/create_huggingface_dataset.py
+++ b/benchmark/AIME2025/create_huggingface_dataset.py
@@ -23,6 +23,7 @@
which is standard in mathematical competition contexts.
"""
+
def create_aime2025_dataset():
"""
Process the AIME2025 dataset and convert it to the standardized benchmark format.
@@ -30,10 +31,10 @@ def create_aime2025_dataset():
# Load the original dataset
dataset = load_dataset("yentinglin/aime_2025")
df = dataset["train"].to_pandas()
-
+
# Create the standardized format
processed_data = []
-
+
for _, row in df.iterrows():
# Format the problem with clear instructions about the boxed answer format
context = f"""Solve the following AIME (American Invitational Mathematics Examination) problem.
@@ -43,43 +44,50 @@ def create_aime2025_dataset():
Instructions:
- The answer should be an integer between 0 and 999
- You must wrap your final answer in \\boxed{{...}} format"""
-
+
question = "What is the answer to this problem?"
-
+
# The answer prefix encourages the model to show work before the final answer
answer_prefix = ""
-
+
# Convert answer to list format (some benchmarks expect this)
- answer = [str(row['answer'])]
-
- processed_data.append({
- 'context': context,
- 'question': question,
- 'answer_prefix': answer_prefix,
- 'answer': answer,
- 'task': 'aime2025',
- 'max_new_tokens': 32000, # Allow comprehensive step-by-step solutions
- 'problem_id': row['id'],
- 'year': row['year']
- })
-
+ answer = [str(row["answer"])]
+
+ processed_data.append(
+ {
+ "context": context,
+ "question": question,
+ "answer_prefix": answer_prefix,
+ "answer": answer,
+ "task": "aime2025",
+ "max_new_tokens": 32000, # Allow comprehensive step-by-step solutions
+ "problem_id": row["id"],
+ "year": row["year"],
+ }
+ )
+
# Convert to DataFrame and then to Dataset
processed_df = pd.DataFrame(processed_data)
-
+
# Select only the required columns for the benchmark
- final_df = processed_df[['context', 'question', 'answer_prefix', 'answer', 'task', 'max_new_tokens']]
-
+ final_df = processed_df[
+ ["context", "question", "answer_prefix", "answer", "task", "max_new_tokens"]
+ ]
+
return Dataset.from_pandas(final_df)
+
if __name__ == "__main__":
# Create the processed dataset
processed_dataset = create_aime2025_dataset()
-
+
# Push to hub (you would need to set up your own repo)
# For now, we'll just save locally or use the existing dataset
print(f"Processed {len(processed_dataset)} AIME2025 problems")
print("Sample processed example:")
print(processed_dataset[0])
-
+
# Optionally save locally for testing
- processed_dataset.push_to_hub("xAlg-AI/att-hub-aime2025", config_name=f"aime2025", split="test")
+ processed_dataset.push_to_hub(
+ "xAlg-AI/att-hub-aime2025", config_name=f"aime2025", split="test"
+ )
diff --git a/benchmark/AIME2025/example_usage.py b/benchmark/AIME2025/example_usage.py
index 5fe3d38e..952faf3f 100644
--- a/benchmark/AIME2025/example_usage.py
+++ b/benchmark/AIME2025/example_usage.py
@@ -8,6 +8,7 @@
import sys
import os
+
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from AIME2025.create_huggingface_dataset import create_aime2025_dataset
@@ -19,21 +20,21 @@ def show_sample_problems():
"""Display sample problems from the AIME2025 dataset."""
print("AIME2025 Sample Problems")
print("=" * 50)
-
+
dataset = create_aime2025_dataset()
-
+
# Show first 3 problems
for i in range(min(3, len(dataset))):
example = dataset[i]
print(f"\nProblem {i+1}:")
print("-" * 30)
-
+
# Extract just the problem statement from the context
- context = example['context']
+ context = example["context"]
problem_start = context.find("Problem: ") + len("Problem: ")
problem_end = context.find("\n\nInstructions:")
problem = context[problem_start:problem_end]
-
+
print(f"Problem: {problem}")
print(f"Answer: {example['answer'][0]}")
@@ -43,50 +44,51 @@ def simulate_model_responses():
print("\n" + "=" * 50)
print("Simulated Model Evaluation")
print("=" * 50)
-
+
# Create some simulated model responses
simulated_responses = [
{
- 'predicted_answer': "Let me work through this step by step.\n\nAfter calculating the bases, I find that the sum is \\boxed{70}.",
- 'answer': ['70'] # Correct
+ "predicted_answer": "Let me work through this step by step.\n\nAfter calculating the bases, I find that the sum is \\boxed{70}.",
+ "answer": ["70"], # Correct
},
{
- 'predicted_answer': "This is a complex problem. After working through it, the answer is \\boxed{42}.",
- 'answer': ['588'] # Wrong
+ "predicted_answer": "This is a complex problem. After working through it, the answer is \\boxed{42}.",
+ "answer": ["588"], # Wrong
},
{
- 'predicted_answer': "The calculation gives us 588 as the final result.",
- 'answer': ['588'] # Correct, but no boxed format
+ "predicted_answer": "The calculation gives us 588 as the final result.",
+ "answer": ["588"], # Correct, but no boxed format
},
{
- 'predicted_answer': "Answer: 16",
- 'answer': ['16'] # Correct, no boxed format
+ "predicted_answer": "Answer: 16",
+ "answer": ["16"], # Correct, no boxed format
},
{
- 'predicted_answer': "This problem is too difficult to solve.",
- 'answer': ['100'] # No answer extracted
- }
+ "predicted_answer": "This problem is too difficult to solve.",
+ "answer": ["100"], # No answer extracted
+ },
]
-
+
# Create DataFrame
df = pd.DataFrame(simulated_responses)
-
+
# Calculate metrics
metrics = calculate_metrics(df)
-
+
print("Simulated Results:")
print(f"Total problems: {len(simulated_responses)}")
print(f"Exact match accuracy: {metrics['exact_match']:.1%}")
print(f"Answer extraction rate: {metrics['extraction_rate']:.1%}")
print(f"Boxed format usage: {metrics['boxed_format_rate']:.1%}")
-
+
print("\nDetailed breakdown:")
for i, response in enumerate(simulated_responses):
from AIME2025.calculate_metrics import extract_numerical_answer
- extracted = extract_numerical_answer(response['predicted_answer'])
- correct = extracted in response['answer']
+
+ extracted = extract_numerical_answer(response["predicted_answer"])
+ correct = extracted in response["answer"]
status = "โ Correct" if correct else "โ Wrong"
-
+
print(f"Problem {i+1}: {status}")
print(f" Expected: {response['answer'][0]}")
print(f" Extracted: '{extracted}'")
@@ -99,12 +101,15 @@ def show_usage_instructions():
print("=" * 50)
print("Usage Instructions")
print("=" * 50)
-
+
print("\n1. Command Line Usage:")
- print(" python evaluation/evaluate.py --dataset aime2025 --base_model meta-llama/Llama-3.1-8B-Instruct")
-
+ print(
+ " python evaluation/evaluate.py --dataset aime2025 --base_model meta-llama/Llama-3.1-8B-Instruct"
+ )
+
print("\n2. Programmatic Usage:")
- print("""
+ print(
+ """
from evaluation.evaluate import evaluate
# Run evaluation
@@ -114,10 +119,12 @@ def show_usage_instructions():
max_new_tokens=4096,
num_samples=10 # Optional: evaluate on subset
)
- """)
-
+ """
+ )
+
print("\n3. Custom Evaluation:")
- print("""
+ print(
+ """
from evaluation.AIME2025 import create_aime2025_dataset, calculate_metrics
import pandas as pd
@@ -134,10 +141,12 @@ def show_usage_instructions():
})
metrics = calculate_metrics(results_df)
print(metrics)
- """)
-
+ """
+ )
+
print("\n4. Expected Output Format:")
- print("""
+ print(
+ """
Models should respond with step-by-step reasoning and wrap
the final answer in \\boxed{...} format:
@@ -150,7 +159,8 @@ def show_usage_instructions():
[mathematical reasoning...]
Therefore, the sum of all valid bases is \\boxed{70}."
- """)
+ """
+ )
def main():
@@ -158,11 +168,11 @@ def main():
show_sample_problems()
simulate_model_responses()
show_usage_instructions()
-
+
print("\n" + "=" * 50)
print("AIME2025 Benchmark Ready!")
print("=" * 50)
if __name__ == "__main__":
- main()
\ No newline at end of file
+ main()
diff --git a/benchmark/AIME2025/test_aime2025.py b/benchmark/AIME2025/test_aime2025.py
index 0a9ae638..44571803 100644
--- a/benchmark/AIME2025/test_aime2025.py
+++ b/benchmark/AIME2025/test_aime2025.py
@@ -8,11 +8,16 @@
import sys
import os
+
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import pandas as pd
from AIME2025.create_huggingface_dataset import create_aime2025_dataset
-from AIME2025.calculate_metrics import calculate_metrics, extract_numerical_answer, analyze_errors
+from AIME2025.calculate_metrics import (
+ calculate_metrics,
+ extract_numerical_answer,
+ analyze_errors,
+)
def test_dataset_creation():
@@ -20,20 +25,27 @@ def test_dataset_creation():
print("=" * 60)
print("Testing AIME2025 Dataset Creation")
print("=" * 60)
-
+
dataset = create_aime2025_dataset()
print(f"โ Successfully created dataset with {len(dataset)} examples")
-
+
# Check the structure
sample = dataset[0]
- required_keys = ['context', 'question', 'answer_prefix', 'answer', 'task', 'max_new_tokens']
-
+ required_keys = [
+ "context",
+ "question",
+ "answer_prefix",
+ "answer",
+ "task",
+ "max_new_tokens",
+ ]
+
for key in required_keys:
if key in sample:
print(f"โ Required key '{key}' present")
else:
print(f"โ Required key '{key}' missing")
-
+
# Show a sample
print("\nSample example:")
print(f"Context (first 200 chars): {sample['context'][:200]}...")
@@ -41,7 +53,7 @@ def test_dataset_creation():
print(f"Answer: {sample['answer']}")
print(f"Task: {sample['task']}")
print(f"Max new tokens: {sample['max_new_tokens']}")
-
+
return dataset
@@ -50,7 +62,7 @@ def test_answer_extraction():
print("\n" + "=" * 60)
print("Testing Answer Extraction")
print("=" * 60)
-
+
test_cases = [
("\\boxed{42}", "42", "Standard boxed format"),
("The answer is \\boxed{123}", "123", "Boxed with prefix"),
@@ -63,7 +75,7 @@ def test_answer_extraction():
("The answer is 1000", "", "Out of range (should be empty)"),
("Multiple numbers: 42, 123, 456", "456", "Multiple numbers (last valid one)"),
]
-
+
for input_text, expected, description in test_cases:
extracted = extract_numerical_answer(input_text)
status = "โ" if extracted == expected else "โ"
@@ -80,48 +92,54 @@ def test_metrics_calculation():
print("=" * 60)
print("Testing Metrics Calculation")
print("=" * 60)
-
+
# Create test data with known outcomes
test_data = {
- 'predicted_answer': [
+ "predicted_answer": [
"Let me solve this step by step.\n\nThe answer is \\boxed{70}", # Correct, boxed
- "After calculation, I get \\boxed{42}", # Wrong answer, boxed
- "The solution is 123", # Correct, no boxed
- "Answer: 999", # Correct, no boxed
- "This problem is too complex to solve", # No answer
- "The result is \\boxed{500}", # Correct, boxed
+ "After calculation, I get \\boxed{42}", # Wrong answer, boxed
+ "The solution is 123", # Correct, no boxed
+ "Answer: 999", # Correct, no boxed
+ "This problem is too complex to solve", # No answer
+ "The result is \\boxed{500}", # Correct, boxed
+ ],
+ "answer": [
+ ["70"], # Match
+ ["123"], # No match (predicted 42)
+ ["123"], # Match
+ ["999"], # Match
+ ["100"], # No match (no prediction)
+ ["500"], # Match
],
- 'answer': [
- ['70'], # Match
- ['123'], # No match (predicted 42)
- ['123'], # Match
- ['999'], # Match
- ['100'], # No match (no prediction)
- ['500'], # Match
- ]
}
-
+
test_df = pd.DataFrame(test_data)
metrics = calculate_metrics(test_df)
-
+
print("Calculated metrics:")
for key, value in metrics.items():
print(f" {key}: {value}")
-
+
# Expected results:
# - exact_match: 4/6 = 0.667 (problems 1, 3, 4, 6 correct)
# - extraction_rate: 5/6 = 0.833 (all except problem 5)
# - boxed_format_rate: 3/6 = 0.5 (problems 1, 2, 6)
-
- expected_exact_match = 4/6
- expected_extraction_rate = 5/6
- expected_boxed_rate = 3/6
-
+
+ expected_exact_match = 4 / 6
+ expected_extraction_rate = 5 / 6
+ expected_boxed_rate = 3 / 6
+
print(f"\nValidation:")
- print(f"โ Exact match: {metrics['exact_match']:.3f} (expected: {expected_exact_match:.3f})")
- print(f"โ Extraction rate: {metrics['extraction_rate']:.3f} (expected: {expected_extraction_rate:.3f})")
- print(f"โ Boxed format rate: {metrics['boxed_format_rate']:.3f} (expected: {expected_boxed_rate:.3f})")
-
+ print(
+ f"โ Exact match: {metrics['exact_match']:.3f} (expected: {expected_exact_match:.3f})"
+ )
+ print(
+ f"โ Extraction rate: {metrics['extraction_rate']:.3f} (expected: {expected_extraction_rate:.3f})"
+ )
+ print(
+ f"โ Boxed format rate: {metrics['boxed_format_rate']:.3f} (expected: {expected_boxed_rate:.3f})"
+ )
+
# Test error analysis
errors = analyze_errors(test_df)
print(f"\nError analysis:")
@@ -134,18 +152,18 @@ def test_integration():
print("\n" + "=" * 60)
print("Testing Integration")
print("=" * 60)
-
+
try:
# Test that the benchmark can be imported from the main evaluation module
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
-
+
# This would normally import from evaluate.py, but we'll skip due to dependencies
print("โ Integration test skipped (requires full environment)")
print(" The benchmark has been properly integrated into evaluate.py")
print(" - Added to DATASET_DICT as 'aime2025': 'yentinglin/aime_2025'")
print(" - Added to SCORER_DICT as 'aime2025': aime2025_scorer")
print(" - Added special handling for dataset processing")
-
+
except Exception as e:
print(f"โ Integration test failed: {e}")
@@ -154,20 +172,22 @@ def main():
"""Run all tests."""
print("AIME2025 Benchmark Test Suite")
print("=" * 60)
-
+
# Run all tests
dataset = test_dataset_creation()
test_answer_extraction()
test_metrics_calculation()
test_integration()
-
+
print("\n" + "=" * 60)
print("Test Suite Complete")
print("=" * 60)
print("The AIME2025 benchmark is ready for use!")
print("\nTo run the benchmark:")
- print(" python evaluation/evaluate.py --dataset aime2025 --base_model ")
+ print(
+ " python evaluation/evaluate.py --dataset aime2025 --base_model "
+ )
if __name__ == "__main__":
- main()
\ No newline at end of file
+ main()
diff --git a/benchmark/base.py b/benchmark/base.py
index 2a7c3a87..bb4c90d9 100644
--- a/benchmark/base.py
+++ b/benchmark/base.py
@@ -40,10 +40,10 @@ class Benchmark(ABC):
... all_datasets = ["task1", "task2"]
... benchmark_name = "my_benchmark"
... huggingface_dataset_id = "my_org/my_dataset"
- ...
+ ...
... def post_run_evaluate(self, results_df):
... return {"accuracy": 0.95}
- >>>
+ >>>
>>> benchmark = MyBenchmark(subsets_to_run=["task1"])
>>> results = benchmark.run_benchmark(adapter, result_dir="/path/to/results", generation_kwargs={"max_new_tokens": 50}, request_kwargs={"max_context_length": 1024})
"""
@@ -63,11 +63,17 @@ def __init__(self, subsets_to_run: Optional[List[str]] = None) -> None:
ValueError: If any subset in subsets_to_run is not in all_datasets.
"""
if not self.all_datasets:
- raise ValueError(f"Subclass {self.__class__.__name__} must define all_datasets")
+ raise ValueError(
+ f"Subclass {self.__class__.__name__} must define all_datasets"
+ )
if not self.benchmark_name:
- raise ValueError(f"Subclass {self.__class__.__name__} must define benchmark_name")
+ raise ValueError(
+ f"Subclass {self.__class__.__name__} must define benchmark_name"
+ )
if not self.huggingface_dataset_id:
- raise ValueError(f"Subclass {self.__class__.__name__} must define huggingface_dataset_id")
+ raise ValueError(
+ f"Subclass {self.__class__.__name__} must define huggingface_dataset_id"
+ )
if subsets_to_run is None:
self.subsets_to_run = self.all_datasets.copy()
@@ -112,7 +118,7 @@ def _load_datasets(self) -> pd.DataFrame:
# Load the full dataset
dataset = load_dataset(self.huggingface_dataset_id, split="test")
df: pd.DataFrame = dataset.to_pandas()
-
+
# Filter to only include subsets we want to run
if "task" in df.columns:
# Filter by task column if it exists
@@ -121,11 +127,13 @@ def _load_datasets(self) -> pd.DataFrame:
# If no task column, assume the dataset only contains our subsets
# This is a simplified assumption based on user guidance
pass
-
+
return df
-
+
except Exception as e:
- raise Exception(f"Failed to load dataset {self.huggingface_dataset_id}: {str(e)}")
+ raise Exception(
+ f"Failed to load dataset {self.huggingface_dataset_id}: {str(e)}"
+ )
def _validate_dataset_size(self, df: pd.DataFrame) -> None:
"""Validate dataset size and warn if too large.
@@ -137,15 +145,15 @@ def _validate_dataset_size(self, df: pd.DataFrame) -> None:
warnings.warn(
f"Dataset has {len(df)} rows (>10K). Repository not expected to handle "
"large datasets. If needed, request this feature.",
- UserWarning
+ UserWarning,
)
def _process_all_requests(
- self,
- adapter: ModelAdapter,
+ self,
+ adapter: ModelAdapter,
dataset_df: pd.DataFrame,
generation_kwargs: Dict[str, Any],
- request_kwargs: Dict[str, Any]
+ request_kwargs: Dict[str, Any],
) -> pd.DataFrame:
"""Process all samples through the model adapter using context grouping for efficiency.
@@ -164,47 +172,60 @@ def _process_all_requests(
# Truncate dataset to max_requests
dataset_df = dataset_df.head(max_requests)
-
-
+
# Group by context for efficiency (following HashAttention approach)
df_context = dataset_df.groupby("context")
-
- for context, df_group in tqdm(df_context, desc="Processing contexts", total=dataset_df["context"].nunique()):
+
+ for context, df_group in tqdm(
+ df_context,
+ desc="Processing contexts",
+ total=dataset_df["context"].nunique(),
+ ):
questions: List[str] = df_group["question"].to_list()
-
+
try:
# Create request using current adapter interface (simplified)
answer_prefix = df_group["answer_prefix"].iloc[0]
- request: Request = Request(context=context, questions=questions, answer_prefix=answer_prefix)
-
+ request: Request = Request(
+ context=context, questions=questions, answer_prefix=answer_prefix
+ )
+
# Process through adapter
- response: RequestResponse = adapter.process_request(request, generation_kwargs, request_kwargs)
-
+ response: RequestResponse = adapter.process_request(
+ request, generation_kwargs, request_kwargs
+ )
+
# Assign responses back to DataFrame
if isinstance(response.responses, list):
- dataset_df.loc[df_group.index, "predicted_answer"] = response.responses
+ dataset_df.loc[df_group.index, "predicted_answer"] = (
+ response.responses
+ )
else:
# Single response case
- dataset_df.loc[df_group.index, "predicted_answer"] = [response.responses] * len(df_group)
-
+ dataset_df.loc[df_group.index, "predicted_answer"] = [
+ response.responses
+ ] * len(df_group)
+
# Memory cleanup for large contexts
if torch.cuda.is_available():
torch.cuda.empty_cache()
-
+
except Exception as e:
# Log error but continue processing other contexts
print(f"Error processing context (length {len(context)}): {str(e)}")
# Fill with empty responses for failed contexts
- dataset_df.loc[df_group.index, "predicted_answer"] = [""] * len(df_group)
-
+ dataset_df.loc[df_group.index, "predicted_answer"] = [""] * len(
+ df_group
+ )
+
return dataset_df
def run_benchmark(
- self,
- adapter: ModelAdapter,
+ self,
+ adapter: ModelAdapter,
result_dir: str,
generation_kwargs: Optional[Dict[str, Any]] = None,
- request_kwargs: Optional[Dict[str, Any]] = None
+ request_kwargs: Optional[Dict[str, Any]] = None,
) -> Dict[str, Any]:
"""Main orchestration method for running complete benchmark.
@@ -222,54 +243,60 @@ def run_benchmark(
generation_kwargs = {}
if request_kwargs is None:
request_kwargs = {}
-
+
# Create result directory if it doesn't exist
result_path: Path = Path(result_dir)
result_path.mkdir(parents=True, exist_ok=True)
-
+
# Load datasets
print(f"Loading {self.benchmark_name} datasets: {self.subsets_to_run}")
dataset_df: pd.DataFrame = self._load_datasets()
print(f"Loaded {len(dataset_df)} samples")
# Validate dataset size
self._validate_dataset_size(dataset_df)
-
+
# Process all requests through the adapter
print("Processing requests through adapter...")
- results_df: pd.DataFrame = self._process_all_requests(adapter, dataset_df, generation_kwargs, request_kwargs)
-
+ results_df: pd.DataFrame = self._process_all_requests(
+ adapter, dataset_df, generation_kwargs, request_kwargs
+ )
+
# Compute evaluation metrics
print("Computing evaluation metrics...")
metrics: Dict[str, Any] = self.post_run_evaluate(results_df)
-
+
# Save results
raw_results_path: Path = result_path / "raw_results.csv"
save_dataframe_to_csv(results_df, str(raw_results_path), index=False)
print(f"Saved raw results to {raw_results_path}")
-
+
# Save metrics
metrics_path: Path = result_path / "metrics.json"
with open(metrics_path, "w") as f:
json.dump(metrics, f, indent=2)
print(f"Saved metrics to {metrics_path}")
-
+
# Save configuration parameters
config_path: Path = result_path / "config.json"
-
+
config_data = {
- "model_kwargs": make_serializable(getattr(adapter, 'model_kwargs', {})),
- "tokenizer_kwargs": make_serializable(getattr(adapter, 'tokenizer_kwargs', {})),
- "sparse_attention_config": make_serializable(getattr(adapter, 'sparse_attention_config', None)),
+ "model_kwargs": make_serializable(getattr(adapter, "model_kwargs", {})),
+ "tokenizer_kwargs": make_serializable(
+ getattr(adapter, "tokenizer_kwargs", {})
+ ),
+ "sparse_attention_config": make_serializable(
+ getattr(adapter, "sparse_attention_config", None)
+ ),
"generation_kwargs": make_serializable(generation_kwargs),
"request_kwargs": make_serializable(request_kwargs),
"benchmark_name": self.benchmark_name,
"subsets_to_run": self.subsets_to_run,
- "huggingface_dataset_id": self.huggingface_dataset_id
+ "huggingface_dataset_id": self.huggingface_dataset_id,
}
with open(config_path, "w") as f:
json.dump(config_data, f, indent=2)
print(f"Saved configuration to {config_path}")
-
+
return metrics
@abstractmethod
@@ -282,7 +309,7 @@ def post_run_evaluate(self, results_df: pd.DataFrame) -> Dict[str, Any]:
Args:
results_df: DataFrame containing input data and model outputs with columns:
- context: The input context
- - question: The input question
+ - question: The input question
- predicted_answer: Model's predicted answer
- Plus any other columns from the original dataset
@@ -290,6 +317,3 @@ def post_run_evaluate(self, results_df: pd.DataFrame) -> Dict[str, Any]:
Dictionary containing computed metrics (e.g., {"accuracy": 0.95, "f1": 0.88}).
"""
pass
-
-
-
diff --git a/benchmark/benchmark_registry.py b/benchmark/benchmark_registry.py
index bbc5bdf8..47d19d41 100644
--- a/benchmark/benchmark_registry.py
+++ b/benchmark/benchmark_registry.py
@@ -18,45 +18,49 @@
def register_benchmark(name: Optional[str] = None, aliases: Optional[List[str]] = None):
"""Decorator to automatically register benchmark classes.
-
+
This decorator allows benchmark classes to register themselves automatically,
eliminating the need for manual registry maintenance.
-
+
Args:
name: Custom name for the benchmark (defaults to class.benchmark_name)
aliases: Optional list of alternative names for the benchmark
-
+
Example:
>>> @register_benchmark("my_benchmark", aliases=["my_bench", "mb"])
>>> class MyBenchmark(Benchmark):
... benchmark_name = "my_benchmark"
... # ... rest of implementation
"""
+
def decorator(benchmark_class: type) -> type:
# Get benchmark name from parameter, class attribute, or class name
if name is not None:
benchmark_name = name
- elif hasattr(benchmark_class, 'benchmark_name') and benchmark_class.benchmark_name:
+ elif (
+ hasattr(benchmark_class, "benchmark_name")
+ and benchmark_class.benchmark_name
+ ):
benchmark_name = benchmark_class.benchmark_name
else:
benchmark_name = benchmark_class.__name__.lower()
-
+
# Register main name
_BENCHMARK_REGISTRY[benchmark_name.lower()] = benchmark_class
-
+
# Register aliases
if aliases:
for alias in aliases:
_BENCHMARK_REGISTRY[alias.lower()] = benchmark_class
-
+
return benchmark_class
-
+
return decorator
def get_registered_benchmarks() -> Dict[str, type]:
"""Get all registered benchmark classes.
-
+
Returns:
Dictionary mapping benchmark names to their classes
"""
@@ -65,7 +69,7 @@ def get_registered_benchmarks() -> Dict[str, type]:
def get_available_benchmark_names() -> List[str]:
"""Get list of all available benchmark names.
-
+
Returns:
Sorted list of registered benchmark names
"""
@@ -73,61 +77,65 @@ def get_available_benchmark_names() -> List[str]:
def create_benchmark_instance(
- benchmark_name: str,
- subsets: Optional[List[str]] = None
+ benchmark_name: str, subsets: Optional[List[str]] = None
) -> Benchmark:
"""Factory function to create benchmark instances by name.
-
+
Uses the automatic registry to instantiate benchmark classes.
No manual maintenance required - benchmarks register themselves.
-
+
Args:
benchmark_name: Name of the benchmark to create
subsets: Optional list of subsets to run for the benchmark
-
+
Returns:
Instantiated benchmark object
-
+
Raises:
ValueError: If benchmark_name is not registered
-
+
Example:
>>> benchmark = create_benchmark_instance("longbench", ["narrativeqa"])
>>> benchmark = create_benchmark_instance("mock_benchmark")
"""
# Normalize benchmark name
benchmark_name = benchmark_name.strip().lower()
-
+
if benchmark_name not in _BENCHMARK_REGISTRY:
available_benchmarks: List[str] = get_available_benchmark_names()
raise ValueError(
f"Unknown benchmark '{benchmark_name}'. Available benchmarks: {available_benchmarks}"
)
-
+
benchmark_class = _BENCHMARK_REGISTRY[benchmark_name]
-
+
try:
# Create instance with optional subsets
benchmark_instance: Benchmark = benchmark_class(subsets_to_run=subsets)
return benchmark_instance
-
+
except Exception as e:
raise RuntimeError(f"Failed to instantiate benchmark '{benchmark_name}': {e}")
def ensure_benchmarks_loaded() -> None:
"""Ensure all benchmark modules are loaded to trigger registration.
-
+
This function imports all benchmark modules to ensure their decorators
have been executed and they're registered in the global registry.
"""
import benchmark
-
+
# Get all modules in the benchmark package
benchmark_package_path = benchmark.__path__
-
+
for importer, module_name, ispkg in pkgutil.iter_modules(benchmark_package_path):
- if not ispkg and module_name != '__init__' and module_name != 'base' and module_name != 'benchmark_registry':
+ if (
+ not ispkg
+ and module_name != "__init__"
+ and module_name != "base"
+ and module_name != "benchmark_registry"
+ ):
try:
# Import the module to trigger @register_benchmark decorators
module_full_name = f"benchmark.{module_name}"
@@ -139,27 +147,29 @@ def ensure_benchmarks_loaded() -> None:
continue
-def validate_benchmark_config(benchmark_name: str, subsets: Optional[List[str]] = None) -> None:
+def validate_benchmark_config(
+ benchmark_name: str, subsets: Optional[List[str]] = None
+) -> None:
"""Validate a benchmark configuration against available benchmarks.
-
+
This function checks if the benchmark exists and if specified subsets are valid.
-
+
Args:
benchmark_name: Name of the benchmark to validate
subsets: Optional list of subsets to validate
-
+
Raises:
ValueError: If benchmark or subsets are invalid
-
+
Example:
>>> validate_benchmark_config("longbench", ["narrativeqa"]) # Passes validation
- >>>
+ >>>
>>> validate_benchmark_config("invalid_benchmark") # Raises ValueError
"""
try:
# Create temporary benchmark instance to validate
benchmark = create_benchmark_instance(benchmark_name, subsets=None)
-
+
# Validate subsets if specified
if subsets is not None:
available_datasets: List[str] = benchmark.get_available_datasets()
@@ -169,23 +179,23 @@ def validate_benchmark_config(benchmark_name: str, subsets: Optional[List[str]]
f"Invalid subsets for {benchmark_name}: {invalid_subsets}. "
f"Available datasets: {available_datasets}"
)
-
+
except Exception as e:
raise ValueError(f"Invalid benchmark configuration: {e}")
def get_benchmark_subsets(benchmark_name: str) -> Optional[List[str]]:
"""Get available subsets for a benchmark using the standard interface.
-
+
This is a utility function that can help users determine what subsets are
available for a given benchmark using the standard get_available_datasets() method.
-
+
Args:
benchmark_name: Name of the benchmark to inspect
-
+
Returns:
List of available subset names, or None if benchmark doesn't support subsets
-
+
Example:
>>> subsets = get_benchmark_subsets("longbench")
>>> print(subsets) # ['narrativeqa', 'qasper', 'multifieldqa_en', ...]
@@ -193,8 +203,8 @@ def get_benchmark_subsets(benchmark_name: str) -> Optional[List[str]]:
try:
benchmark_instance = create_benchmark_instance(benchmark_name)
return benchmark_instance.get_available_datasets()
-
+
except Exception as e:
# If we can't instantiate the benchmark or get subset info, return None
logging.warning(f"Could not get subsets for benchmark '{benchmark_name}': {e}")
- return None
\ No newline at end of file
+ return None
diff --git a/benchmark/executor.py b/benchmark/executor.py
index 49ce54c5..445093a7 100644
--- a/benchmark/executor.py
+++ b/benchmark/executor.py
@@ -19,8 +19,8 @@
from sparse_attention_hub.metric_logging.logger import MicroMetricLogger
# Set multiprocessing start method to 'spawn' for CUDA compatibility
-if multiprocessing.get_start_method(allow_none=True) != 'spawn':
- multiprocessing.set_start_method('spawn', force=True)
+if multiprocessing.get_start_method(allow_none=True) != "spawn":
+ multiprocessing.set_start_method("spawn", force=True)
from sparse_attention_hub.sparse_attention.base import SparseAttentionConfig
@@ -34,12 +34,8 @@
BenchmarkFailure,
filter_existing_results,
generate_benchmark_stubs,
-
-)
-from .benchmark_registry import (
- ensure_benchmarks_loaded,
- create_benchmark_instance
)
+from .benchmark_registry import ensure_benchmarks_loaded, create_benchmark_instance
from .utils.gpu import (
cleanup_gpu_memory,
@@ -56,14 +52,15 @@
@contextmanager
def set_gpu(gpu_id: int):
"""Context manager to set the current CUDA device.
-
+
Args:
gpu_id: The GPU device ID to set as current device
-
+
Yields:
None: The context manager yields control to the with block
"""
import torch
+
torch.cuda.set_device(gpu_id)
yield
@@ -72,22 +69,24 @@ def _signal_handler(signum: int, frame: Any) -> None:
"""Handle shutdown signals gracefully."""
global _shutdown_requested
_shutdown_requested = True
- logging.getLogger(__name__).warning(f"Received signal {signum}, initiating graceful shutdown...")
+ logging.getLogger(__name__).warning(
+ f"Received signal {signum}, initiating graceful shutdown..."
+ )
def _categorize_error(error: Exception, context: str = "") -> str:
"""Categorize errors for better handling and reporting.
-
+
Args:
error: The exception that occurred
context: Additional context about where the error occurred
-
+
Returns:
Error category string for classification
"""
error_type = type(error).__name__
error_msg = str(error).lower()
-
+
# GPU-related errors
if any(keyword in error_msg for keyword in ["cuda", "gpu", "out of memory", "oom"]):
if "out of memory" in error_msg or "oom" in error_msg:
@@ -96,32 +95,41 @@ def _categorize_error(error: Exception, context: str = "") -> str:
return "GPU_CUDA_ERROR"
else:
return "GPU_ERROR"
-
+
# Model loading errors
- elif any(keyword in error_msg for keyword in ["model", "huggingface", "transformers", "pretrained"]):
+ elif any(
+ keyword in error_msg
+ for keyword in ["model", "huggingface", "transformers", "pretrained"]
+ ):
if "not found" in error_msg or "doesn't exist" in error_msg:
return "MODEL_NOT_FOUND"
elif "tokenizer" in error_msg:
return "TOKENIZER_ERROR"
else:
return "MODEL_LOADING_ERROR"
-
+
# Dataset/benchmark errors
elif any(keyword in error_msg for keyword in ["dataset", "benchmark", "subset"]):
return "DATASET_ERROR"
-
+
# Network/connection errors
- elif any(keyword in error_msg for keyword in ["connection", "timeout", "network", "http"]):
+ elif any(
+ keyword in error_msg for keyword in ["connection", "timeout", "network", "http"]
+ ):
return "NETWORK_ERROR"
-
+
# File system errors
- elif any(keyword in error_msg for keyword in ["file", "directory", "permission", "disk"]):
+ elif any(
+ keyword in error_msg for keyword in ["file", "directory", "permission", "disk"]
+ ):
return "FILESYSTEM_ERROR"
-
+
# Multiprocessing errors
- elif any(keyword in error_msg for keyword in ["queue", "process", "multiprocessing"]):
+ elif any(
+ keyword in error_msg for keyword in ["queue", "process", "multiprocessing"]
+ ):
return "MULTIPROCESSING_ERROR"
-
+
# Default categories
elif isinstance(error, TimeoutError):
return "TIMEOUT_ERROR"
@@ -140,17 +148,17 @@ def _benchmark_worker(
error_queue: multiprocessing.Queue,
timeout_per_benchmark: float = 3600.0,
micro_metric_logger_max_records: Optional[int] = None,
- micro_metric_logger_sampling_factor: float = 1.0
+ micro_metric_logger_sampling_factor: float = 1.0,
) -> None:
"""Worker function to execute benchmarks with dynamic GPU allocation.
-
+
This function runs in a separate process and handles:
1. Acquiring GPUs from the shared pool
2. Creating model adapters with sparse attention configurations
3. Executing benchmarks with proper error handling
4. Releasing GPUs back to the pool
5. Reporting results and errors
-
+
Args:
stub_queue: Queue containing BenchmarkStub instances to process
gpu_pool: Shared queue containing available GPU IDs
@@ -162,11 +170,11 @@ def _benchmark_worker(
"""
worker_id = os.getpid()
logger = logging.getLogger(f"{__name__}.worker_{worker_id}")
-
+
# Set up signal handlers for graceful shutdown
signal.signal(signal.SIGTERM, _signal_handler)
signal.signal(signal.SIGINT, _signal_handler)
-
+
# Track resources for cleanup
current_gpu_id: Optional[int] = None
adapter = None
@@ -177,7 +185,7 @@ def _benchmark_worker(
if _shutdown_requested:
logger.info(f"Worker {worker_id}: Shutdown requested, exiting")
break
-
+
# Step 1: Get next benchmark stub from queue
try:
stub: BenchmarkStub = stub_queue.get(timeout=1.0) # 1 second timeout
@@ -185,127 +193,154 @@ def _benchmark_worker(
# No more work to do
logger.info(f"Worker {worker_id}: No more work, shutting down")
break
-
+
# Check for sentinel value (shutdown signal)
if stub is None:
logger.info(f"Worker {worker_id}: Received shutdown signal, exiting")
break
-
+
# Step 2: Acquire GPU from pool
try:
current_gpu_id = acquire_gpu_from_pool(gpu_pool, timeout=30.0)
- logger.info(f"Worker {worker_id}: Acquired GPU {current_gpu_id} for {stub.model_name}/{stub.sparse_config_name}/{stub.benchmark_name}")
+ logger.info(
+ f"Worker {worker_id}: Acquired GPU {current_gpu_id} for {stub.model_name}/{stub.sparse_config_name}/{stub.benchmark_name}"
+ )
except Exception as e:
error_category = _categorize_error(e, "GPU acquisition")
error_msg = f"Failed to acquire GPU for {stub.model_name}/{stub.sparse_config_name}/{stub.benchmark_name}: {e}"
logger.error(f"Worker {worker_id}: {error_msg}")
- error_queue.put(BenchmarkFailure(
- stub=stub,
- error_message=error_msg,
- error_type=error_category,
- execution_time=0.0
- ))
+ error_queue.put(
+ BenchmarkFailure(
+ stub=stub,
+ error_message=error_msg,
+ error_type=error_category,
+ execution_time=0.0,
+ )
+ )
continue
# Step 3: Run the benchmark on the current GPU
start_time = time.time()
execution_time = 0.0 # Initialize execution_time
-
+
try:
with set_gpu(current_gpu_id):
- logger.debug(f"Worker {worker_id}: Set CUDA device to {current_gpu_id}")
+ logger.debug(
+ f"Worker {worker_id}: Set CUDA device to {current_gpu_id}"
+ )
execution_success = False
# Create model adapter with sparse attention config
- logger.info(f"Worker {worker_id}: Creating model adapter for {stub.model_name}")
-
+ logger.info(
+ f"Worker {worker_id}: Creating model adapter for {stub.model_name}"
+ )
+
# Import here to avoid issues with multiprocessing
from sparse_attention_hub.adapters.huggingface import ModelAdapterHF
-
+
adapter = ModelAdapterHF(
model_name=stub.model_name,
sparse_attention_config=stub.sparse_attention_config,
model_kwargs=stub.adapter_config.model_kwargs,
- tokenizer_kwargs=stub.adapter_config.tokenizer_kwargs
+ tokenizer_kwargs=stub.adapter_config.tokenizer_kwargs,
)
-
+
# Create benchmark instance
- logger.info(f"Worker {worker_id}: Creating benchmark instance for {stub.benchmark_name}")
+ logger.info(
+ f"Worker {worker_id}: Creating benchmark instance for {stub.benchmark_name}"
+ )
benchmark = create_benchmark_instance(
benchmark_name=stub.benchmark_name,
- subsets=[stub.subset] if stub.subset else None
+ subsets=[stub.subset] if stub.subset else None,
)
-
+
# Execute benchmark
- logger.info(f"Worker {worker_id}: Executing benchmark {stub.benchmark_name} on GPU {current_gpu_id}")
+ logger.info(
+ f"Worker {worker_id}: Executing benchmark {stub.benchmark_name} on GPU {current_gpu_id}"
+ )
metric_logger = MicroMetricLogger()
metric_logger.configure_logging(
- log_path=stub.result_dir,
- enabled_metrics=["research_attention_density", "research_attention_output_error"],
+ log_path=stub.result_dir,
+ enabled_metrics=[
+ "research_attention_density",
+ "research_attention_output_error",
+ ],
max_records=micro_metric_logger_max_records,
- sampling_factor=micro_metric_logger_sampling_factor
+ sampling_factor=micro_metric_logger_sampling_factor,
)
metrics = benchmark.run_benchmark(
adapter=adapter,
result_dir=stub.result_dir,
generation_kwargs=stub.generation_kwargs,
- request_kwargs=stub.request_kwargs
+ request_kwargs=stub.request_kwargs,
)
metric_logger.flush()
-
+
execution_time = time.time() - start_time
execution_success = True
-
+
# Report successful result
result = BenchmarkResult(
stub=stub,
metrics=metrics,
execution_time=execution_time,
- success=True
+ success=True,
)
result_queue.put(result)
-
- logger.info(f"Worker {worker_id}: Successfully completed {stub.model_name}/{stub.sparse_config_name}/{stub.benchmark_name} in {execution_time:.2f}s")
+
+ logger.info(
+ f"Worker {worker_id}: Successfully completed {stub.model_name}/{stub.sparse_config_name}/{stub.benchmark_name} in {execution_time:.2f}s"
+ )
except Exception as e:
- execution_time = time.time() - start_time # Calculate execution time even on failure
+ execution_time = (
+ time.time() - start_time
+ ) # Calculate execution time even on failure
error_category = _categorize_error(e, "benchmark execution")
error_msg = f"Benchmark execution failed: {e}"
logger.error(f"Worker {worker_id}: {error_msg}")
-
+
# Log detailed error information for debugging
- logger.debug(f"Worker {worker_id}: Error details: {traceback.format_exc()}")
-
+ logger.debug(
+ f"Worker {worker_id}: Error details: {traceback.format_exc()}"
+ )
+
# Report failure
failure = BenchmarkFailure(
stub=stub,
error_message=error_msg,
error_type=error_category,
- execution_time=execution_time
+ execution_time=execution_time,
)
error_queue.put(failure)
continue
-
+
finally:
# Step 4: Cleanup and release GPU
- _cleanup_worker_resources(gpu_pool, current_gpu_id, adapter, logger, worker_id)
+ _cleanup_worker_resources(
+ gpu_pool, current_gpu_id, adapter, logger, worker_id
+ )
current_gpu_id = None
adapter = None
-
+
except Exception as worker_error:
logger.error(f"Worker {worker_id}: Critical worker error: {worker_error}")
- logger.debug(f"Worker {worker_id}: Critical error details: {traceback.format_exc()}")
-
+ logger.debug(
+ f"Worker {worker_id}: Critical error details: {traceback.format_exc()}"
+ )
+
# Cleanup any remaining resources
_cleanup_worker_resources(gpu_pool, current_gpu_id, adapter, logger, worker_id)
-
+
# Report worker failure to error queue
- error_queue.put({
- "worker_id": worker_id,
- "error_type": "WORKER_CRITICAL_ERROR",
- "error_message": str(worker_error),
- "error_traceback": traceback.format_exc()
- })
+ error_queue.put(
+ {
+ "worker_id": worker_id,
+ "error_type": "WORKER_CRITICAL_ERROR",
+ "error_message": str(worker_error),
+ "error_traceback": traceback.format_exc(),
+ }
+ )
def _cleanup_worker_resources(
@@ -313,10 +348,10 @@ def _cleanup_worker_resources(
gpu_id: Optional[int],
adapter: Any,
logger: logging.Logger,
- worker_id: int
+ worker_id: int,
) -> None:
"""Clean up worker resources including GPU memory and adapter.
-
+
Args:
gpu_pool: GPU pool queue to return GPU to
gpu_id: GPU ID to release (can be None) - this is the actual GPU ID from the pool
@@ -331,53 +366,60 @@ def _cleanup_worker_resources(
# Force garbage collection of adapter
del adapter
import gc
+
gc.collect()
logger.debug(f"Worker {worker_id}: Cleaned up model adapter")
except Exception as e:
logger.warning(f"Worker {worker_id}: Failed to cleanup adapter: {e}")
-
+
# Clean up GPU memory and release GPU
if gpu_id is not None:
try:
cleanup_gpu_memory(gpu_id)
- logger.debug(f"Worker {worker_id}: Cleaned up GPU memory for GPU {gpu_id}")
-
+ logger.debug(
+ f"Worker {worker_id}: Cleaned up GPU memory for GPU {gpu_id}"
+ )
+
# Release GPU back to pool (always use the original GPU ID for the pool)
release_gpu_to_pool(gpu_pool, gpu_id)
logger.debug(f"Worker {worker_id}: Released GPU {gpu_id} back to pool")
-
+
except Exception as cleanup_error:
- logger.warning(f"Worker {worker_id}: GPU cleanup failed for GPU {gpu_id}: {cleanup_error}")
+ logger.warning(
+ f"Worker {worker_id}: GPU cleanup failed for GPU {gpu_id}: {cleanup_error}"
+ )
# Try to release GPU even if cleanup failed
try:
release_gpu_to_pool(gpu_pool, gpu_id)
except Exception:
- logger.error(f"Worker {worker_id}: Failed to release GPU {gpu_id} to pool")
-
+ logger.error(
+ f"Worker {worker_id}: Failed to release GPU {gpu_id} to pool"
+ )
+
except Exception as e:
logger.error(f"Worker {worker_id}: Error during resource cleanup: {e}")
class BenchmarkExecutor:
"""Parallel benchmark executor with dynamic GPU allocation and resumability.
-
- Executes benchmark experiments across a model ร sparse_attention_config ร
+
+ Executes benchmark experiments across a model ร sparse_attention_config ร
benchmark ร subset matrix with parallel GPU execution and automatic resumability.
-
+
Features:
- Dynamic GPU allocation from specified gpu_ids
- Subset-level resumability (skip completed experiments)
- Parallel execution with configurable concurrency
- Comprehensive error handling and logging
- Result aggregation and summary generation
-
+
Example:
>>> executor = BenchmarkExecutor(
... gpu_ids=[0, 1, 2, 3],
... max_concurrent_runs=4,
... base_result_dir="./benchmark_results"
... )
- >>>
+ >>>
>>> results = executor.run_benchmark_matrix(
... model_names=["model1", "model2"],
... sparse_attention_configs=[("dense", None), ("sparse", config)],
@@ -385,7 +427,7 @@ class BenchmarkExecutor:
... adapter_config=AdapterConfig()
... )
"""
-
+
def __init__(
self,
gpu_ids: List[int],
@@ -396,11 +438,13 @@ def __init__(
required_result_files: Optional[List[str]] = None,
timeout_per_benchmark: float = 360000.0, # 100 hours default
verbose: bool = True,
- micro_metric_logger_max_records: Optional[int] = None, # Maximum number of metric events to log
+ micro_metric_logger_max_records: Optional[
+ int
+ ] = None, # Maximum number of metric events to log
micro_metric_logger_sampling_factor: float = 1.0, # Probability of logging each metric event (0.0-1.0)
):
"""Initialize the BenchmarkExecutor.
-
+
Args:
gpu_ids: List of GPU device IDs to use for parallel execution
max_concurrent_runs: Maximum number of concurrent benchmark runs
@@ -412,61 +456,70 @@ def __init__(
verbose: Whether to enable verbose logging
micro_metric_logger_max_records: Maximum number of metric events to log (None for unlimited)
micro_metric_logger_sampling_factor: Probability of logging each metric event (0.0-1.0)
-
+
Raises:
ValueError: If configuration parameters are invalid
RuntimeError: If GPU validation fails
"""
# Validate and store configuration
self.gpu_ids = self._validate_gpu_ids(gpu_ids)
- self.max_concurrent_runs = self._validate_max_concurrent_runs(max_concurrent_runs)
+ self.max_concurrent_runs = self._validate_max_concurrent_runs(
+ max_concurrent_runs
+ )
self.base_result_dir = Path(base_result_dir).resolve()
self.enable_resumability = enable_resumability
self.result_file_validation = result_file_validation
self.required_result_files = required_result_files or ["raw_results.csv"]
self.timeout_per_benchmark = timeout_per_benchmark
self.verbose = verbose
-
+
# Metric logging configuration
self.micro_metric_logger_max_records = micro_metric_logger_max_records
- self.micro_metric_logger_sampling_factor = max(0.0, min(1.0, micro_metric_logger_sampling_factor)) # Clamp to [0.0, 1.0]
-
+ self.micro_metric_logger_sampling_factor = max(
+ 0.0, min(1.0, micro_metric_logger_sampling_factor)
+ ) # Clamp to [0.0, 1.0]
+
# Initialize logging
self._setup_logging()
-
+
# Set up signal handlers for graceful shutdown
self._setup_signal_handlers()
-
+
# Validate GPU availability
self._validate_gpu_setup()
-
+
# Load all available benchmarks
ensure_benchmarks_loaded()
-
-
self.logger.info(f"BenchmarkExecutor initialized:")
self.logger.info(f" GPU IDs: {self.gpu_ids}")
self.logger.info(f" Max concurrent runs: {self.max_concurrent_runs}")
self.logger.info(f" Base result directory: {self.base_result_dir}")
- self.logger.info(f" Resumability: {'enabled' if self.enable_resumability else 'disabled'}")
- self.logger.info(f" Metric logging - Max records: {self.micro_metric_logger_max_records if self.micro_metric_logger_max_records else 'unlimited'}")
- self.logger.info(f" Metric logging - Sampling factor: {self.micro_metric_logger_sampling_factor}")
-
+ self.logger.info(
+ f" Resumability: {'enabled' if self.enable_resumability else 'disabled'}"
+ )
+ self.logger.info(
+ f" Metric logging - Max records: {self.micro_metric_logger_max_records if self.micro_metric_logger_max_records else 'unlimited'}"
+ )
+ self.logger.info(
+ f" Metric logging - Sampling factor: {self.micro_metric_logger_sampling_factor}"
+ )
+
def _setup_signal_handlers(self) -> None:
"""Set up signal handlers for graceful shutdown."""
signal.signal(signal.SIGTERM, _signal_handler)
signal.signal(signal.SIGINT, _signal_handler)
-
+
# Register cleanup function to run at exit
import atexit
+
atexit.register(self._cleanup_on_exit)
-
+
def _cleanup_on_exit(self) -> None:
"""Cleanup function called when the process exits."""
- if hasattr(self, 'logger'):
+ if hasattr(self, "logger"):
self.logger.info("BenchmarkExecutor cleanup on exit")
-
+
# Reset global shutdown flag
global _shutdown_requested
_shutdown_requested = False
@@ -478,13 +531,13 @@ def run_benchmark_matrix(
benchmark_configs: List[BenchmarkConfig],
adapter_config: AdapterConfig,
generation_kwargs: Optional[Dict[str, Any]] = None,
- request_kwargs: Optional[Dict[str, Any]] = None
+ request_kwargs: Optional[Dict[str, Any]] = None,
) -> ExecutionResults:
"""Execute benchmark matrix with parallel GPU execution.
-
- Runs all combinations of model_names ร sparse_attention_configs ร
+
+ Runs all combinations of model_names ร sparse_attention_configs ร
benchmark_configs ร subsets in parallel across available GPUs.
-
+
Args:
model_names: List of model names to benchmark
sparse_attention_configs: List of (name, config) tuples for sparse attention
@@ -492,10 +545,10 @@ def run_benchmark_matrix(
adapter_config: Configuration for model adapter
generation_kwargs: Optional generation parameters
request_kwargs: Optional request processing parameters
-
+
Returns:
ExecutionResults containing comprehensive execution information
-
+
Example:
>>> results = executor.run_benchmark_matrix(
... model_names=["meta-llama/Llama-3.1-8B-Instruct"],
@@ -507,13 +560,13 @@ def run_benchmark_matrix(
"""
# Initialize execution
self.logger.info("Starting benchmark matrix execution")
-
+
try:
# Step 1: Validate input parameters
self._validate_matrix_parameters(
model_names, sparse_attention_configs, benchmark_configs, adapter_config
)
-
+
# Step 2: Generate benchmark stubs (matrix expansion)
self.logger.info("Generating benchmark matrix...")
all_stubs = generate_benchmark_stubs(
@@ -523,14 +576,16 @@ def run_benchmark_matrix(
adapter_config=adapter_config,
generation_kwargs=generation_kwargs,
request_kwargs=request_kwargs,
- base_result_dir=str(self.base_result_dir)
+ base_result_dir=str(self.base_result_dir),
)
-
+
total_combinations = len(all_stubs)
self.logger.info(f"Generated {total_combinations} benchmark combinations:")
- self.logger.info(f" {len(model_names)} models ร {len(sparse_attention_configs)} sparse configs ร "
- f"{sum(len(b.subsets) if b.subsets else 1 for b in benchmark_configs)} benchmark-subsets")
-
+ self.logger.info(
+ f" {len(model_names)} models ร {len(sparse_attention_configs)} sparse configs ร "
+ f"{sum(len(b.subsets) if b.subsets else 1 for b in benchmark_configs)} benchmark-subsets"
+ )
+
# Step 3: Filter for resumability (if enabled)
if self.enable_resumability:
self.logger.info("Checking for existing results (resumability)...")
@@ -538,50 +593,56 @@ def run_benchmark_matrix(
all_stubs,
check_result_files=self.result_file_validation,
required_files=self.required_result_files,
- verbose=self.verbose
+ verbose=self.verbose,
+ )
+
+ self.logger.info(
+ f"Resumability check: {len(completed_stubs)} completed, {len(pending_stubs)} pending"
)
-
- self.logger.info(f"Resumability check: {len(completed_stubs)} completed, {len(pending_stubs)} pending")
else:
pending_stubs = all_stubs
completed_stubs = []
- self.logger.info("Resumability disabled - will execute all combinations")
-
+ self.logger.info(
+ "Resumability disabled - will execute all combinations"
+ )
+
# Step 4: Early exit if nothing to do
if not pending_stubs:
- self.logger.info("No pending benchmarks to execute. All experiments already completed!")
-
+ self.logger.info(
+ "No pending benchmarks to execute. All experiments already completed!"
+ )
+
execution_summary = {
"total_combinations": total_combinations,
"models": model_names,
"sparse_configs": [name for name, _ in sparse_attention_configs],
- "benchmarks": [cfg.benchmark_name for cfg in benchmark_configs]
+ "benchmarks": [cfg.benchmark_name for cfg in benchmark_configs],
}
-
+
progress = ExecutionProgress(
total_stubs=total_combinations,
skipped_stubs=len(completed_stubs),
completed_stubs=0,
- failed_stubs=0
+ failed_stubs=0,
)
-
+
return ExecutionResults(
execution_summary=execution_summary,
individual_results=[],
failed_executions=[],
execution_time=0.0,
- progress=progress
+ progress=progress,
)
-
-
# Step 6: Execute pending benchmarks with worker processes
- self.logger.info(f"Executing {len(pending_stubs)} pending benchmarks with {self.max_concurrent_runs} workers...")
-
+ self.logger.info(
+ f"Executing {len(pending_stubs)} pending benchmarks with {self.max_concurrent_runs} workers..."
+ )
+
start_time = time.time()
results = self._execute_with_workers(pending_stubs)
execution_time = time.time() - start_time
-
+
# Step 7: Aggregate results
execution_summary = {
"total_combinations": total_combinations,
@@ -590,101 +651,112 @@ def run_benchmark_matrix(
"benchmarks": [cfg.benchmark_name for cfg in benchmark_configs],
"execution_time_seconds": execution_time,
"gpu_ids_used": self.gpu_ids,
- "max_concurrent_runs": self.max_concurrent_runs
+ "max_concurrent_runs": self.max_concurrent_runs,
}
-
+
progress = ExecutionProgress(
total_stubs=total_combinations,
skipped_stubs=len(completed_stubs),
completed_stubs=len(results.individual_results),
- failed_stubs=len(results.failed_executions)
+ failed_stubs=len(results.failed_executions),
)
-
+
final_results = ExecutionResults(
execution_summary=execution_summary,
individual_results=results.individual_results,
failed_executions=results.failed_executions,
execution_time=execution_time,
- progress=progress
+ progress=progress,
)
-
+
self.logger.info(f"Benchmark matrix execution completed:")
self.logger.info(f" Total time: {execution_time:.2f}s")
self.logger.info(f" Successful: {len(results.individual_results)}")
self.logger.info(f" Failed: {len(results.failed_executions)}")
self.logger.info(f" Skipped: {len(completed_stubs)}")
-
+
return final_results
-
+
except Exception as e:
self.logger.error(f"Benchmark execution failed: {e}")
raise
-
- def _execute_with_workers(self, pending_stubs: List[BenchmarkStub]) -> ExecutionResults:
+
+ def _execute_with_workers(
+ self, pending_stubs: List[BenchmarkStub]
+ ) -> ExecutionResults:
"""Execute benchmark stubs using worker processes.
-
+
This method implements comprehensive process pool management with:
- Dynamic work distribution through shared queues
- Graceful process startup and shutdown
- Real-time result collection and error handling
- Process health monitoring and recovery
-
+
Args:
pending_stubs: List of benchmark stubs to execute
-
+
Returns:
ExecutionResults containing all results and failures
"""
- self.logger.info(f"Starting execution with {len(pending_stubs)} stubs using {self.max_concurrent_runs} workers")
-
+ self.logger.info(
+ f"Starting execution with {len(pending_stubs)} stubs using {self.max_concurrent_runs} workers"
+ )
+
# Step 1: Create and configure shared queues
- stub_queue = multiprocessing.Queue(maxsize=len(pending_stubs) + 100) # Buffer for queue operations
+ stub_queue = multiprocessing.Queue(
+ maxsize=len(pending_stubs) + 100
+ ) # Buffer for queue operations
gpu_pool = create_gpu_pool(self.gpu_ids)
result_queue = multiprocessing.Queue()
error_queue = multiprocessing.Queue()
-
+
# Step 2: Populate work queue with all stubs
self.logger.info("Populating work queue...")
for stub in pending_stubs:
stub_queue.put(stub)
-
+
# Add sentinel values to signal workers to stop
for _ in range(self.max_concurrent_runs):
stub_queue.put(None) # Sentinel value
-
+
# Step 3: Create and start worker processes
workers = []
worker_pids = []
-
+
self.logger.info(f"Starting {self.max_concurrent_runs} worker processes...")
for i in range(self.max_concurrent_runs):
worker = multiprocessing.Process(
target=_benchmark_worker,
- args=(stub_queue,
- gpu_pool,
- result_queue,
- error_queue,
- self.timeout_per_benchmark,
- self.micro_metric_logger_max_records,
- self.micro_metric_logger_sampling_factor
- ),
- name=f"benchmark_worker_{i}"
+ args=(
+ stub_queue,
+ gpu_pool,
+ result_queue,
+ error_queue,
+ self.timeout_per_benchmark,
+ self.micro_metric_logger_max_records,
+ self.micro_metric_logger_sampling_factor,
+ ),
+ name=f"benchmark_worker_{i}",
+ )
+ worker.daemon = (
+ True # Ensure workers are terminated when main process exits
)
- worker.daemon = True # Ensure workers are terminated when main process exits
worker.start()
workers.append(worker)
worker_pids.append(worker.pid)
- self.logger.debug(f"Started worker {i+1}/{self.max_concurrent_runs} (PID: {worker.pid})")
-
+ self.logger.debug(
+ f"Started worker {i+1}/{self.max_concurrent_runs} (PID: {worker.pid})"
+ )
+
self.logger.info(f"All {self.max_concurrent_runs} workers started successfully")
-
+
# Step 4: Monitor and collect results in real-time
individual_results: List[BenchmarkResult] = []
failed_executions: List[BenchmarkFailure] = []
active_workers = set(worker_pids)
-
+
self.logger.info("Monitoring worker processes and collecting results...")
-
+
try:
# Monitor workers and collect results until all work is complete
while active_workers:
@@ -699,47 +771,74 @@ def _execute_with_workers(self, pending_stubs: List[BenchmarkStub]) -> Execution
if worker.pid in active_workers:
active_workers.remove(worker.pid)
completed_workers.append(worker)
-
+
if worker.exitcode != 0:
- self.logger.warning(f"Worker {worker.pid} exited with code {worker.exitcode}")
+ self.logger.warning(
+ f"Worker {worker.pid} exited with code {worker.exitcode}"
+ )
else:
- self.logger.debug(f"Worker {worker.pid} completed successfully")
-
+ self.logger.debug(
+ f"Worker {worker.pid} completed successfully"
+ )
+
# Collect any available results (non-blocking)
- self._collect_available_results(result_queue, error_queue, individual_results, failed_executions)
-
+ self._collect_available_results(
+ result_queue, error_queue, individual_results, failed_executions
+ )
+
# Log progress periodically
- if len(individual_results) + len(failed_executions) > 0 and (len(individual_results) + len(failed_executions)) % 5 == 0:
- self.logger.info(f"Progress: {len(individual_results)} completed, {len(failed_executions)} failed, {len(active_workers)} workers active")
-
+ if (
+ len(individual_results) + len(failed_executions) > 0
+ and (len(individual_results) + len(failed_executions)) % 5 == 0
+ ):
+ self.logger.info(
+ f"Progress: {len(individual_results)} completed, {len(failed_executions)} failed, {len(active_workers)} workers active"
+ )
+
# Small delay to prevent busy waiting
time.sleep(0.1)
-
+
# Step 5: Final result collection
self.logger.info("All workers completed, collecting final results...")
- self._collect_available_results(result_queue, error_queue, individual_results, failed_executions)
-
+ self._collect_available_results(
+ result_queue, error_queue, individual_results, failed_executions
+ )
+
except KeyboardInterrupt:
- self.logger.warning("Received interrupt signal, initiating graceful shutdown...")
- self._graceful_shutdown(workers, stub_queue, gpu_pool, result_queue, error_queue)
+ self.logger.warning(
+ "Received interrupt signal, initiating graceful shutdown..."
+ )
+ self._graceful_shutdown(
+ workers, stub_queue, gpu_pool, result_queue, error_queue
+ )
raise
-
+
except Exception as e:
error_category = _categorize_error(e, "main execution")
self.logger.error(f"Error during execution ({error_category}): {e}")
self.logger.debug(f"Execution error details: {traceback.format_exc()}")
- self._graceful_shutdown(workers, stub_queue, gpu_pool, result_queue, error_queue)
+ self._graceful_shutdown(
+ workers, stub_queue, gpu_pool, result_queue, error_queue
+ )
raise
-
+
finally:
# Step 6: Cleanup resources
- self._cleanup_resources(workers, stub_queue, gpu_pool, result_queue, error_queue)
-
+ self._cleanup_resources(
+ workers, stub_queue, gpu_pool, result_queue, error_queue
+ )
+
# Step 7: Final summary
total_processed = len(individual_results) + len(failed_executions)
- self.logger.info(f"Execution completed: {len(individual_results)} successful, {len(failed_executions)} failed")
- self.logger.info(f"Success rate: {len(individual_results)/total_processed*100:.1f}%" if total_processed > 0 else "No work processed")
-
+ self.logger.info(
+ f"Execution completed: {len(individual_results)} successful, {len(failed_executions)} failed"
+ )
+ self.logger.info(
+ f"Success rate: {len(individual_results)/total_processed*100:.1f}%"
+ if total_processed > 0
+ else "No work processed"
+ )
+
return ExecutionResults(
execution_summary={}, # Will be filled by caller
individual_results=individual_results,
@@ -748,19 +847,19 @@ def _execute_with_workers(self, pending_stubs: List[BenchmarkStub]) -> Execution
progress=ExecutionProgress(
total_stubs=len(pending_stubs),
completed_stubs=len(individual_results),
- failed_stubs=len(failed_executions)
- )
+ failed_stubs=len(failed_executions),
+ ),
)
-
+
def _collect_available_results(
self,
result_queue: multiprocessing.Queue,
error_queue: multiprocessing.Queue,
individual_results: List[BenchmarkResult],
- failed_executions: List[BenchmarkFailure]
+ failed_executions: List[BenchmarkFailure],
) -> None:
"""Collect all available results from queues without blocking.
-
+
Args:
result_queue: Queue containing successful benchmark results
error_queue: Queue containing benchmark failures
@@ -774,7 +873,7 @@ def _collect_available_results(
individual_results.append(result)
except Empty:
break
-
+
# Collect all available errors
while True:
try:
@@ -786,17 +885,17 @@ def _collect_available_results(
self.logger.error(f"Worker error: {error}")
except Empty:
break
-
+
def _graceful_shutdown(
self,
workers: List[multiprocessing.Process],
stub_queue: multiprocessing.Queue,
gpu_pool: multiprocessing.Queue,
result_queue: multiprocessing.Queue,
- error_queue: multiprocessing.Queue
+ error_queue: multiprocessing.Queue,
) -> None:
"""Perform graceful shutdown of worker processes and queues.
-
+
Args:
workers: List of worker processes to terminate
stub_queue: Work queue to close
@@ -805,7 +904,7 @@ def _graceful_shutdown(
error_queue: Error queue to close
"""
self.logger.info("Initiating graceful shutdown...")
-
+
try:
# Signal workers to stop by adding sentinel values
sentinel_count = 0
@@ -816,9 +915,9 @@ def _graceful_shutdown(
except Exception as e:
self.logger.debug(f"Could not add sentinel to queue: {e}")
break # Queue is full, stop adding sentinels
-
+
self.logger.info(f"Added {sentinel_count} shutdown signals to work queue")
-
+
# Wait for workers to finish with timeout
self.logger.info("Waiting for workers to finish...")
for i, worker in enumerate(workers):
@@ -829,16 +928,20 @@ def _graceful_shutdown(
worker.terminate()
worker.join(timeout=5.0)
if worker.is_alive():
- self.logger.error(f"Failed to terminate worker {worker.pid}, killing process")
+ self.logger.error(
+ f"Failed to terminate worker {worker.pid}, killing process"
+ )
worker.kill()
worker.join(timeout=2.0)
else:
- self.logger.debug(f"Worker {worker.pid} terminated successfully")
+ self.logger.debug(
+ f"Worker {worker.pid} terminated successfully"
+ )
except Exception as e:
self.logger.error(f"Error during worker {worker.pid} shutdown: {e}")
-
+
self.logger.info("Graceful shutdown completed")
-
+
except Exception as e:
self.logger.error(f"Error during graceful shutdown: {e}")
# Force cleanup of remaining workers
@@ -848,17 +951,17 @@ def _graceful_shutdown(
worker.kill()
except Exception:
pass
-
+
def _cleanup_resources(
self,
workers: List[multiprocessing.Process],
stub_queue: multiprocessing.Queue,
gpu_pool: multiprocessing.Queue,
result_queue: multiprocessing.Queue,
- error_queue: multiprocessing.Queue
+ error_queue: multiprocessing.Queue,
) -> None:
"""Clean up all multiprocessing resources.
-
+
Args:
workers: List of worker processes
stub_queue: Work queue to close
@@ -867,7 +970,7 @@ def _cleanup_resources(
error_queue: Error queue to close
"""
self.logger.debug("Cleaning up multiprocessing resources...")
-
+
# Close all queues
try:
stub_queue.close()
@@ -876,7 +979,7 @@ def _cleanup_resources(
error_queue.close()
except Exception as e:
self.logger.warning(f"Error closing queues: {e}")
-
+
# Join any remaining workers
for worker in workers:
if worker.is_alive():
@@ -885,17 +988,17 @@ def _cleanup_resources(
worker.join(timeout=5.0)
if worker.is_alive():
worker.kill()
-
+
self.logger.debug("Resource cleanup completed")
-
+
def _validate_gpu_ids(self, gpu_ids: List[int]) -> List[int]:
"""Validate and return GPU IDs."""
if not gpu_ids:
raise ValueError("gpu_ids cannot be empty")
-
+
if not all(isinstance(gpu_id, int) and gpu_id >= 0 for gpu_id in gpu_ids):
raise ValueError("All GPU IDs must be non-negative integers")
-
+
# Remove duplicates while preserving order
seen = set()
unique_gpu_ids = []
@@ -903,34 +1006,38 @@ def _validate_gpu_ids(self, gpu_ids: List[int]) -> List[int]:
if gpu_id not in seen:
seen.add(gpu_id)
unique_gpu_ids.append(gpu_id)
-
+
return unique_gpu_ids
-
+
def _validate_max_concurrent_runs(self, max_concurrent_runs: int) -> int:
"""Validate and return max_concurrent_runs."""
if not isinstance(max_concurrent_runs, int) or max_concurrent_runs <= 0:
raise ValueError("max_concurrent_runs must be a positive integer")
-
- if max_concurrent_runs > len(self.gpu_ids) if hasattr(self, 'gpu_ids') else True:
+
+ if (
+ max_concurrent_runs > len(self.gpu_ids)
+ if hasattr(self, "gpu_ids")
+ else True
+ ):
# This is just a warning - user might want more concurrent runs than GPUs
# for benchmarks that don't require much GPU memory
pass
-
+
return max_concurrent_runs
-
+
def _setup_logging(self) -> None:
"""Setup logging configuration."""
self.logger = logging.getLogger(f"{__name__}.BenchmarkExecutor")
-
+
if self.verbose and not self.logger.handlers:
handler = logging.StreamHandler()
formatter = logging.Formatter(
- '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
+ "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
)
handler.setFormatter(formatter)
self.logger.addHandler(handler)
self.logger.setLevel(logging.INFO)
-
+
def _validate_gpu_setup(self) -> None:
"""Validate GPU availability."""
try:
@@ -939,47 +1046,53 @@ def _validate_gpu_setup(self) -> None:
except (RuntimeError, ValueError) as e:
self.logger.error(f"GPU validation failed: {e}")
raise
-
+
def _validate_matrix_parameters(
self,
model_names: List[str],
sparse_attention_configs: List[Tuple[str, Optional[SparseAttentionConfig]]],
benchmark_configs: List[BenchmarkConfig],
- adapter_config: AdapterConfig
+ adapter_config: AdapterConfig,
) -> None:
"""Validate benchmark matrix parameters."""
if not model_names:
raise ValueError("model_names cannot be empty")
-
+
if not sparse_attention_configs:
raise ValueError("sparse_attention_configs cannot be empty")
-
+
if not benchmark_configs:
raise ValueError("benchmark_configs cannot be empty")
-
+
# Validate model names
for model_name in model_names:
if not isinstance(model_name, str) or not model_name.strip():
raise ValueError(f"Invalid model name: {model_name}")
-
+
# Validate sparse attention configs
for sparse_config in sparse_attention_configs:
if not isinstance(sparse_config, tuple) or len(sparse_config) != 2:
- raise ValueError(f"sparse_attention_configs must be list of (name, config) tuples")
-
+ raise ValueError(
+ f"sparse_attention_configs must be list of (name, config) tuples"
+ )
+
config_name, config_obj = sparse_config
if not isinstance(config_name, str) or not config_name.strip():
- raise ValueError(f"Sparse config name must be non-empty string: {config_name}")
-
+ raise ValueError(
+ f"Sparse config name must be non-empty string: {config_name}"
+ )
+
# Validate benchmark configs
for benchmark_config in benchmark_configs:
if not isinstance(benchmark_config, BenchmarkConfig):
- raise ValueError(f"benchmark_configs must contain BenchmarkConfig instances")
+ raise ValueError(
+ f"benchmark_configs must contain BenchmarkConfig instances"
+ )
# Additional validation could be added here if needed
-
+
# Validate adapter config
if not isinstance(adapter_config, AdapterConfig):
raise ValueError("adapter_config must be an AdapterConfig instance")
# Additional validation could be added here if needed
-
+
self.logger.info("Matrix parameter validation passed")
diff --git a/benchmark/executor_config.py b/benchmark/executor_config.py
index 081d1871..9ef7febf 100644
--- a/benchmark/executor_config.py
+++ b/benchmark/executor_config.py
@@ -25,53 +25,53 @@
acquire_gpu_from_pool,
release_gpu_to_pool,
validate_gpu_for_model,
- cleanup_gpu_memory
+ cleanup_gpu_memory,
)
@dataclass
class BenchmarkConfig:
"""Configuration for a benchmark execution.
-
+
During stub generation, this expands to individual BenchmarkStub per subset.
Each BenchmarkStub will have a single subset value for resumability.
-
+
Attributes:
benchmark_name: Name of the benchmark (e.g., "longbench", "mock_benchmark")
subsets: Optional list of dataset subsets to run (e.g., ["narrativeqa", "qasper"])
If None, runs all available subsets for the benchmark
-
+
Example:
>>> config = BenchmarkConfig("longbench", ["narrativeqa", "qasper"])
>>> # This will create 2 BenchmarkStub instances during matrix expansion
"""
-
+
benchmark_name: str
subsets: Optional[List[str]] = None
-
+
def __post_init__(self) -> None:
"""Validate benchmark configuration after initialization.
-
+
Raises:
ValueError: If benchmark_name is empty or invalid.
"""
if not self.benchmark_name or not self.benchmark_name.strip():
raise ValueError("benchmark_name cannot be empty")
-
+
self.benchmark_name = self.benchmark_name.strip()
-
+
if self.subsets is not None:
# Remove empty strings and strip whitespace
self.subsets = [subset.strip() for subset in self.subsets if subset.strip()]
if not self.subsets:
self.subsets = None
-
+
def validate_with_benchmark_instance(self, benchmark: Benchmark) -> None:
"""Validate subsets against actual benchmark instance.
-
+
Args:
benchmark: Benchmark instance to validate against
-
+
Raises:
ValueError: If any subset is not available in the benchmark.
"""
@@ -88,47 +88,47 @@ def validate_with_benchmark_instance(self, benchmark: Benchmark) -> None:
@dataclass
class AdapterConfig:
"""Base configuration for model adapters (sparse attention config is part of matrix).
-
+
Note: sparse_attention_config is now part of the execution matrix, not base config.
This allows testing different sparse attention configurations with the same adapter base.
-
+
Attributes:
adapter_name: Name of the adapter type (default: "huggingface")
model_kwargs: Additional keyword arguments for model creation
tokenizer_kwargs: Additional keyword arguments for tokenizer creation
-
+
Example:
>>> config = AdapterConfig(
... model_kwargs={"torch_dtype": torch.bfloat16},
... tokenizer_kwargs={"padding_side": "left"}
... )
"""
-
+
adapter_name: str = "huggingface"
model_kwargs: Optional[Dict[str, Any]] = None
tokenizer_kwargs: Optional[Dict[str, Any]] = None
-
+
def __post_init__(self) -> None:
"""Initialize default values and validate configuration."""
if self.model_kwargs is None:
self.model_kwargs = {}
if self.tokenizer_kwargs is None:
self.tokenizer_kwargs = {}
-
+
if not self.adapter_name or not self.adapter_name.strip():
raise ValueError("adapter_name cannot be empty")
-
+
self.adapter_name = self.adapter_name.strip()
@dataclass
class BenchmarkStub:
"""A single benchmark execution task (model ร sparse_config ร benchmark ร subset).
-
+
This represents one atomic execution unit that will be processed by a worker process.
Each stub corresponds to a specific combination of model, sparse attention config,
benchmark, and subset (if applicable).
-
+
Attributes:
model_name: HuggingFace model name (e.g., "meta-llama/Llama-3.1-8B-Instruct")
sparse_config_name: Human-readable name for sparse config (e.g., "dense", "streaming")
@@ -139,7 +139,7 @@ class BenchmarkStub:
generation_kwargs: Parameters for model inference/generation
request_kwargs: Parameters for request processing (e.g., max_context_length)
result_dir: Full path to directory where results should be saved
-
+
Example:
>>> stub = BenchmarkStub(
... model_name="microsoft/Phi-4-mini-instruct",
@@ -153,7 +153,7 @@ class BenchmarkStub:
... result_dir="/path/to/results/phi4_dense_longbench_narrativeqa"
... )
"""
-
+
model_name: str
sparse_config_name: str
sparse_attention_config: Optional[SparseAttentionConfig]
@@ -163,7 +163,7 @@ class BenchmarkStub:
generation_kwargs: Dict[str, Any]
request_kwargs: Dict[str, Any]
result_dir: str
-
+
def __post_init__(self) -> None:
"""Validate benchmark stub configuration."""
if not self.model_name or not self.model_name.strip():
@@ -174,13 +174,13 @@ def __post_init__(self) -> None:
raise ValueError("benchmark_name cannot be empty")
if not self.result_dir or not self.result_dir.strip():
raise ValueError("result_dir cannot be empty")
-
+
# Trim whitespace
self.model_name = self.model_name.strip()
self.sparse_config_name = self.sparse_config_name.strip()
self.benchmark_name = self.benchmark_name.strip()
self.result_dir = self.result_dir.strip()
-
+
if self.subset is not None:
self.subset = self.subset.strip() if self.subset.strip() else None
@@ -188,7 +188,7 @@ def __post_init__(self) -> None:
@dataclass
class BenchmarkResult:
"""Result from a single benchmark execution.
-
+
Attributes:
stub: The benchmark stub that was executed
metrics: Evaluation metrics returned by benchmark.run_benchmark()
@@ -196,7 +196,7 @@ class BenchmarkResult:
success: Whether the benchmark executed successfully
error_message: Error message if execution failed
"""
-
+
stub: BenchmarkStub
metrics: Optional[Dict[str, Any]]
execution_time: float
@@ -207,14 +207,14 @@ class BenchmarkResult:
@dataclass
class BenchmarkFailure:
"""Information about a failed benchmark execution.
-
+
Attributes:
stub: The benchmark stub that failed
error_message: Description of the failure
error_type: Type of error that occurred
execution_time: Time spent before failure in seconds
"""
-
+
stub: BenchmarkStub
error_message: str
error_type: str
@@ -224,7 +224,7 @@ class BenchmarkFailure:
@dataclass
class ExecutionProgress:
"""Tracks execution progress across all processes.
-
+
Attributes:
total_stubs: Total number of benchmark stubs to execute
skipped_stubs: Number of stubs skipped due to existing results (resumability)
@@ -232,30 +232,39 @@ class ExecutionProgress:
failed_stubs: Number of stubs that failed execution
current_executions: Mapping of GPU ID to current task description
"""
-
+
total_stubs: int
skipped_stubs: int = 0
completed_stubs: int = 0
failed_stubs: int = 0
current_executions: Dict[int, str] = field(default_factory=dict)
-
+
@property
def pending_stubs(self) -> int:
"""Calculate number of stubs pending execution."""
- return self.total_stubs - self.skipped_stubs - self.completed_stubs - self.failed_stubs
-
+ return (
+ self.total_stubs
+ - self.skipped_stubs
+ - self.completed_stubs
+ - self.failed_stubs
+ )
+
@property
def completion_percentage(self) -> float:
"""Calculate completion percentage (0-100)."""
if self.total_stubs == 0:
return 100.0
- return (self.completed_stubs + self.failed_stubs) / (self.total_stubs - self.skipped_stubs) * 100.0
+ return (
+ (self.completed_stubs + self.failed_stubs)
+ / (self.total_stubs - self.skipped_stubs)
+ * 100.0
+ )
@dataclass
class ExecutionResults:
"""Aggregated results from benchmark execution.
-
+
Attributes:
execution_summary: Summary metadata about the execution
individual_results: List of successful benchmark results
@@ -263,7 +272,7 @@ class ExecutionResults:
execution_time: Total execution time in seconds
progress: Final progress statistics
"""
-
+
execution_summary: Dict[str, Any]
individual_results: List[BenchmarkResult]
failed_executions: List[BenchmarkFailure]
@@ -283,35 +292,35 @@ class ExecutionResults:
create_benchmark_instance,
ensure_benchmarks_loaded,
validate_benchmark_config as _validate_benchmark_config_base,
- get_benchmark_subsets
+ get_benchmark_subsets,
)
def validate_benchmark_config(config: BenchmarkConfig) -> None:
"""Validate a benchmark configuration against available benchmarks.
-
+
This function checks if the benchmark exists and if specified subsets are valid.
-
+
Args:
config: BenchmarkConfig to validate
-
+
Raises:
ValueError: If benchmark or subsets are invalid
-
+
Example:
>>> config = BenchmarkConfig("longbench", ["narrativeqa"])
>>> validate_benchmark_config(config) # Passes validation
- >>>
+ >>>
>>> config = BenchmarkConfig("invalid_benchmark")
>>> validate_benchmark_config(config) # Raises ValueError
"""
try:
# Create temporary benchmark instance to validate
benchmark = create_benchmark_instance(config.benchmark_name, subsets=None)
-
+
# Validate subsets if specified
config.validate_with_benchmark_instance(benchmark)
-
+
except Exception as e:
raise ValueError(f"Invalid benchmark configuration: {e}")
@@ -320,6 +329,7 @@ def validate_benchmark_config(config: BenchmarkConfig) -> None:
# Matrix Expansion and Benchmark Stub Generation Functions
# =============================================================================
+
def generate_benchmark_stubs(
model_names: List[str],
sparse_attention_configs: List[Tuple[str, Optional[SparseAttentionConfig]]],
@@ -327,13 +337,13 @@ def generate_benchmark_stubs(
adapter_config: AdapterConfig,
generation_kwargs: Optional[Dict[str, Any]] = None,
request_kwargs: Optional[Dict[str, Any]] = None,
- base_result_dir: str = "./benchmark_results"
+ base_result_dir: str = "./benchmark_results",
) -> List[BenchmarkStub]:
"""Generate all benchmark stubs for the model ร sparse_config ร benchmark ร subset matrix.
-
+
Creates BenchmarkStub instances for every combination of:
- model_names ร sparse_attention_configs ร benchmark_configs ร subsets
-
+
Args:
model_names: List of model names to benchmark
sparse_attention_configs: List of (name, config) tuples for sparse attention
@@ -342,13 +352,13 @@ def generate_benchmark_stubs(
generation_kwargs: Optional generation parameters
request_kwargs: Optional request processing parameters
base_result_dir: Base directory for storing results
-
+
Returns:
List of BenchmarkStub instances representing all combinations
-
+
Example:
>>> stubs = generate_benchmark_stubs(
- ... model_names=["model1", "model2"],
+ ... model_names=["model1", "model2"],
... sparse_attention_configs=[("dense", None), ("sparse", config)],
... benchmark_configs=[BenchmarkConfig("longbench", ["narrativeqa"])],
... adapter_config=AdapterConfig()
@@ -356,14 +366,14 @@ def generate_benchmark_stubs(
>>> len(stubs) # 2 models ร 2 configs ร 1 benchmark ร 1 subset = 4
4
"""
-
+
if generation_kwargs is None:
generation_kwargs = {}
if request_kwargs is None:
request_kwargs = {}
-
+
stubs: List[BenchmarkStub] = []
-
+
# Expand the full matrix: model ร sparse_config ร benchmark ร subset
for model_name in model_names:
for sparse_config_name, sparse_attention_config in sparse_attention_configs:
@@ -376,9 +386,9 @@ def generate_benchmark_stubs(
model_name=model_name,
sparse_config_name=sparse_config_name,
benchmark_name=benchmark_config.benchmark_name,
- subset=subset
+ subset=subset,
)
-
+
stub = BenchmarkStub(
model_name=model_name,
sparse_config_name=sparse_config_name,
@@ -388,10 +398,10 @@ def generate_benchmark_stubs(
adapter_config=adapter_config,
generation_kwargs=generation_kwargs.copy(),
request_kwargs=request_kwargs.copy(),
- result_dir=result_dir
+ result_dir=result_dir,
)
stubs.append(stub)
-
+
else:
# Handle benchmarks with no subsets (run full benchmark)
result_dir = construct_result_directory(
@@ -399,9 +409,9 @@ def generate_benchmark_stubs(
model_name=model_name,
sparse_config_name=sparse_config_name,
benchmark_name=benchmark_config.benchmark_name,
- subset=None
+ subset=None,
)
-
+
stub = BenchmarkStub(
model_name=model_name,
sparse_config_name=sparse_config_name,
@@ -411,10 +421,10 @@ def generate_benchmark_stubs(
adapter_config=adapter_config,
generation_kwargs=generation_kwargs.copy(),
request_kwargs=request_kwargs.copy(),
- result_dir=result_dir
+ result_dir=result_dir,
)
stubs.append(stub)
-
+
return stubs
@@ -422,50 +432,50 @@ def filter_existing_results(
stubs: List[BenchmarkStub],
check_result_files: bool = True,
required_files: Optional[List[str]] = None,
- verbose: bool = True
+ verbose: bool = True,
) -> Tuple[List[BenchmarkStub], List[BenchmarkStub]]:
"""Filter benchmark stubs to skip already completed experiments (resumability).
-
+
Checks each stub's result_dir to determine if the experiment has already been
completed. This enables resuming interrupted benchmark runs.
-
+
Args:
stubs: List of benchmark stubs to filter
check_result_files: Whether to check for specific result files (not just directory)
required_files: List of filenames that must exist for completion (default: ["results.json"])
verbose: Whether to log skipped combinations
-
+
Returns:
Tuple of (pending_stubs, completed_stubs) where:
- pending_stubs: Stubs that need to be executed
- completed_stubs: Stubs that have already been completed
-
+
Example:
>>> pending, completed = filter_existing_results(all_stubs)
>>> print(f"Found {len(completed)} completed, {len(pending)} pending")
"""
if required_files is None:
required_files = ["raw_results.csv"]
-
+
pending_stubs: List[BenchmarkStub] = []
completed_stubs: List[BenchmarkStub] = []
-
+
for stub in stubs:
result_path = Path(stub.result_dir)
print(f"Checking result directory: {result_path}", flush=True)
-
+
# Check if result directory exists
if not result_path.exists():
pending_stubs.append(stub)
continue
-
+
# If not checking result files, just check directory existence
if not check_result_files:
completed_stubs.append(stub)
if verbose:
logging.info(f"Skipping completed experiment: {stub.result_dir}")
continue
-
+
# Check for required result files
all_files_exist = True
for required_file in required_files:
@@ -473,20 +483,24 @@ def filter_existing_results(
if not file_path.exists() or not file_path.is_file():
all_files_exist = False
break
-
+
if all_files_exist:
completed_stubs.append(stub)
if verbose:
- logging.info(f"Skipping completed experiment (has {required_files}): {stub.result_dir}")
+ logging.info(
+ f"Skipping completed experiment (has {required_files}): {stub.result_dir}"
+ )
else:
pending_stubs.append(stub)
if verbose:
logging.info(f"Pending experiment (missing results): {stub.result_dir}")
-
+
if verbose:
total = len(stubs)
completed_count = len(completed_stubs)
pending_count = len(completed_stubs)
- logging.info(f"Resumability check: {completed_count}/{total} completed, {pending_count}/{total} pending")
-
- return pending_stubs, completed_stubs
\ No newline at end of file
+ logging.info(
+ f"Resumability check: {completed_count}/{total} completed, {pending_count}/{total} pending"
+ )
+
+ return pending_stubs, completed_stubs
diff --git a/benchmark/infinite_bench/__init__.py b/benchmark/infinite_bench/__init__.py
index 2a5de88d..7f26a1ea 100644
--- a/benchmark/infinite_bench/__init__.py
+++ b/benchmark/infinite_bench/__init__.py
@@ -8,4 +8,4 @@
from .calculate_metrics import calculate_metrics
from .infinite_bench import InfiniteBench
-__all__ = ["calculate_metrics", "InfiniteBench"]
\ No newline at end of file
+__all__ = ["calculate_metrics", "InfiniteBench"]
diff --git a/benchmark/infinite_bench/calculate_metrics.py b/benchmark/infinite_bench/calculate_metrics.py
index e9937896..489d8fb3 100644
--- a/benchmark/infinite_bench/calculate_metrics.py
+++ b/benchmark/infinite_bench/calculate_metrics.py
@@ -365,7 +365,9 @@ def get_score_one(pred: str, label: str, task_name: str, model_name: str) -> flo
"math_calc": get_score_one_math_calc,
}
if task_name == "longbook_sum_eng":
- raise AssertionError("Longbook sum task is not supported due to a conflict with rouge library. ")
+ raise AssertionError(
+ "Longbook sum task is not supported due to a conflict with rouge library. "
+ )
assert task_name in NAME_TO_SCORE_GETTER, f"Invalid task name: {task_name}"
score = NAME_TO_SCORE_GETTER[task_name](pred, label, model_name)
return float(score)
diff --git a/benchmark/infinite_bench/create_huggingface_dataset.py b/benchmark/infinite_bench/create_huggingface_dataset.py
index 60303fa5..363b0413 100644
--- a/benchmark/infinite_bench/create_huggingface_dataset.py
+++ b/benchmark/infinite_bench/create_huggingface_dataset.py
@@ -156,7 +156,8 @@ def update_math_find_context(row):
df["context"] = df.apply(update_math_find_context, axis=1)
df["context"] = df["context"].apply(
lambda x: x.replace(
- "You should answer with only one number, no other words. The largest number of the list is:", ""
+ "You should answer with only one number, no other words. The largest number of the list is:",
+ "",
)
)
elif task == "code_run":
@@ -177,13 +178,19 @@ def update_context(row):
df["context"] = df.apply(update_context, axis=1)
else:
- df["context"] = df["context"].apply(lambda x: context_prefix[task].format(context=x))
+ df["context"] = df["context"].apply(
+ lambda x: context_prefix[task].format(context=x)
+ )
if task in ["longbook_choice_eng", "code_debug"]:
df["question"] = df["question"] + df["options"].apply(
- lambda x: question_template[task].format(OPTION_A=x[0], OPTION_B=x[1], OPTION_C=x[2], OPTION_D=x[3])
+ lambda x: question_template[task].format(
+ OPTION_A=x[0], OPTION_B=x[1], OPTION_C=x[2], OPTION_D=x[3]
+ )
+ )
+ df["answer"] = df.apply(
+ lambda row: ["ABCD"[list(row.options).index(row.answer)]], axis=1
)
- df["answer"] = df.apply(lambda row: ["ABCD"[list(row.options).index(row.answer)]], axis=1)
if task == "kv_retrieval":
# moved to answer prefix
diff --git a/benchmark/infinite_bench/infinite_bench.py b/benchmark/infinite_bench/infinite_bench.py
index 3b1bd50b..9a373e05 100644
--- a/benchmark/infinite_bench/infinite_bench.py
+++ b/benchmark/infinite_bench/infinite_bench.py
@@ -31,34 +31,37 @@ class InfiniteBench(Benchmark):
# All available InfiniteBench datasets
all_datasets: List[str] = [
"passkey",
- "kv_retrieval",
+ "kv_retrieval",
"number_string",
"code_run",
"code_debug",
"math_find",
"longbook_qa_eng",
"longdialogue_qa_eng",
- "longbook_choice_eng"
+ "longbook_choice_eng",
]
-
+
benchmark_name: str = "infinite_bench"
huggingface_dataset_id: str = "MaxJeblick/InfiniteBench"
def _load_datasets(self) -> pd.DataFrame:
"""Load InfiniteBench datasets by individual configs.
-
+
InfiniteBench requires loading each subset as a separate config.
-
+
Returns:
Combined pandas DataFrame with all samples from subsets_to_run.
"""
print(f"Loading InfiniteBench datasets: {self.subsets_to_run}")
dfs = []
-
+
for subset in self.subsets_to_run:
try:
from datasets import load_dataset
- subset_dataset = load_dataset(self.huggingface_dataset_id, subset, split="test")
+
+ subset_dataset = load_dataset(
+ self.huggingface_dataset_id, subset, split="test"
+ )
subset_df = subset_dataset.to_pandas()
subset_df["task"] = subset # Ensure task column exists
dfs.append(subset_df)
@@ -66,12 +69,13 @@ def _load_datasets(self) -> pd.DataFrame:
except Exception as subset_error:
print(f" โ Failed to load {subset}: {str(subset_error)}")
continue
-
+
if not dfs:
raise Exception("No InfiniteBench subsets could be loaded successfully")
-
+
# Combine all subset DataFrames
import pandas as pd
+
combined_df = pd.concat(dfs, ignore_index=True)
print(f"Combined {len(combined_df)} total samples from {len(dfs)} subsets")
return combined_df
@@ -97,7 +101,7 @@ def post_run_evaluate(self, results_df: pd.DataFrame) -> Dict[str, Any]:
task_groups = results_df.groupby("task")
task_scores: Dict[str, float] = {}
all_scores: List[float] = []
-
+
for task_name, task_df in task_groups:
try:
# Use the calculate_metrics function from HashAttention evaluation
@@ -105,21 +109,23 @@ def post_run_evaluate(self, results_df: pd.DataFrame) -> Dict[str, Any]:
task_scores[task_name] = score
all_scores.append(score)
print(f" โ {task_name}: {score:.4f}")
-
+
except Exception as e:
print(f"Error evaluating task {task_name}: {str(e)}")
task_scores[task_name] = 0.0
# Compute overall metrics
overall_score = sum(all_scores) / len(all_scores) if all_scores else 0.0
-
+
overall_metrics: Dict[str, Any] = {
"overall_score": round(overall_score, 4),
- "task_scores": {task: round(score, 4) for task, score in task_scores.items()},
+ "task_scores": {
+ task: round(score, 4) for task, score in task_scores.items()
+ },
"summary": {
"total_tasks": len(task_scores),
- "total_samples": len(results_df)
- }
+ "total_samples": len(results_df),
+ },
}
-
- return overall_metrics
\ No newline at end of file
+
+ return overall_metrics
diff --git a/benchmark/longbench/__init__.py b/benchmark/longbench/__init__.py
index f757aa48..fa3bebde 100644
--- a/benchmark/longbench/__init__.py
+++ b/benchmark/longbench/__init__.py
@@ -8,4 +8,4 @@
from .calculate_metrics import calculate_metrics, calculate_metrics_e
from .longbench import LongBench
-__all__ = ["calculate_metrics", "calculate_metrics_e", "LongBench"]
\ No newline at end of file
+__all__ = ["calculate_metrics", "calculate_metrics_e", "LongBench"]
diff --git a/benchmark/longbench/calculate_metrics.py b/benchmark/longbench/calculate_metrics.py
index 05fb322c..8c491bc5 100644
--- a/benchmark/longbench/calculate_metrics.py
+++ b/benchmark/longbench/calculate_metrics.py
@@ -43,7 +43,12 @@ def scorer_e(dataset, predictions, answers, lengths, all_classes):
if dataset in ["trec", "triviaqa", "samsum", "lsht"]:
prediction = prediction.lstrip("\n").split("\n")[0]
for ground_truth in ground_truths:
- score = max(score, dataset2metric[dataset](prediction, ground_truth, all_classes=all_classes))
+ score = max(
+ score,
+ dataset2metric[dataset](
+ prediction, ground_truth, all_classes=all_classes
+ ),
+ )
if length < 4000:
scores["0-4k"].append(score)
elif length < 8000:
@@ -62,7 +67,12 @@ def scorer(dataset, predictions, answers, all_classes):
if dataset in ["trec", "triviaqa", "samsum", "lsht"]:
prediction = prediction.lstrip("\n").split("\n")[0]
for ground_truth in ground_truths:
- score = max(score, dataset2metric[dataset](prediction, ground_truth, all_classes=all_classes))
+ score = max(
+ score,
+ dataset2metric[dataset](
+ prediction, ground_truth, all_classes=all_classes
+ ),
+ )
total_score += score
return round(100 * total_score / len(predictions), 2)
diff --git a/benchmark/longbench/create_huggingface_dataset.py b/benchmark/longbench/create_huggingface_dataset.py
index f7e2269c..84978b04 100644
--- a/benchmark/longbench/create_huggingface_dataset.py
+++ b/benchmark/longbench/create_huggingface_dataset.py
@@ -123,15 +123,27 @@
if task == "trec":
dataset = dataset.map(
- lambda x: {"input": question_template[task].format(input=x["input"].removesuffix("Type:"))}
+ lambda x: {
+ "input": question_template[task].format(
+ input=x["input"].removesuffix("Type:")
+ )
+ }
)
elif task == "triviaqa":
dataset = dataset.map(
- lambda x: {"input": question_template[task].format(input=x["input"].removesuffix("Answer:"))}
+ lambda x: {
+ "input": question_template[task].format(
+ input=x["input"].removesuffix("Answer:")
+ )
+ }
)
elif task == "samsum":
dataset = dataset.map(
- lambda x: {"input": question_template[task].format(input=x["input"].removesuffix("Summary:"))}
+ lambda x: {
+ "input": question_template[task].format(
+ input=x["input"].removesuffix("Summary:")
+ )
+ }
)
else:
dataset = dataset.map(lambda x: {"input": question_template[task].format(**x)})
@@ -170,15 +182,27 @@
if task == "trec":
dataset = dataset.map(
- lambda x: {"input": question_template[task].format(input=x["input"].removesuffix("Type:"))}
+ lambda x: {
+ "input": question_template[task].format(
+ input=x["input"].removesuffix("Type:")
+ )
+ }
)
elif task == "triviaqa":
dataset = dataset.map(
- lambda x: {"input": question_template[task].format(input=x["input"].removesuffix("Answer:"))}
+ lambda x: {
+ "input": question_template[task].format(
+ input=x["input"].removesuffix("Answer:")
+ )
+ }
)
elif task == "samsum":
dataset = dataset.map(
- lambda x: {"input": question_template[task].format(input=x["input"].removesuffix("Summary:"))}
+ lambda x: {
+ "input": question_template[task].format(
+ input=x["input"].removesuffix("Summary:")
+ )
+ }
)
else:
dataset = dataset.map(lambda x: {"input": question_template[task].format(**x)})
@@ -194,4 +218,6 @@
# Push to hub
dataset = Dataset.from_pandas(df)
- dataset.push_to_hub("xAlg-AI/att-hub-longbench", config_name=f"{task}_e", split="test")
+ dataset.push_to_hub(
+ "xAlg-AI/att-hub-longbench", config_name=f"{task}_e", split="test"
+ )
diff --git a/benchmark/longbench/longbench.py b/benchmark/longbench/longbench.py
index 081fa6fa..d2edc2ef 100644
--- a/benchmark/longbench/longbench.py
+++ b/benchmark/longbench/longbench.py
@@ -33,36 +33,64 @@ class LongBench(Benchmark):
# All available LongBench datasets (22 standard + 13 extended)
all_datasets: List[str] = [
# Standard LongBench datasets
- "narrativeqa", "qasper", "multifieldqa_en", "multifieldqa_zh",
- "hotpotqa", "2wikimqa", "musique", "dureader", "gov_report",
- "qmsum", "multi_news", "vcsum", "trec", "triviaqa", "samsum",
- "lsht", "passage_count", "passage_retrieval_en",
- "passage_retrieval_zh", "lcc", "repobench-p",
+ "narrativeqa",
+ "qasper",
+ "multifieldqa_en",
+ "multifieldqa_zh",
+ "hotpotqa",
+ "2wikimqa",
+ "musique",
+ "dureader",
+ "gov_report",
+ "qmsum",
+ "multi_news",
+ "vcsum",
+ "trec",
+ "triviaqa",
+ "samsum",
+ "lsht",
+ "passage_count",
+ "passage_retrieval_en",
+ "passage_retrieval_zh",
+ "lcc",
+ "repobench-p",
# LongBench-E (extended) datasets with longer contexts
- "qasper_e", "multifieldqa_en_e", "hotpotqa_e", "2wikimqa_e",
- "gov_report_e", "multi_news_e", "trec_e", "triviaqa_e",
- "samsum_e", "passage_count_e", "passage_retrieval_en_e",
- "lcc_e", "repobench-p_e"
+ "qasper_e",
+ "multifieldqa_en_e",
+ "hotpotqa_e",
+ "2wikimqa_e",
+ "gov_report_e",
+ "multi_news_e",
+ "trec_e",
+ "triviaqa_e",
+ "samsum_e",
+ "passage_count_e",
+ "passage_retrieval_en_e",
+ "lcc_e",
+ "repobench-p_e",
]
-
+
benchmark_name: str = "longbench"
huggingface_dataset_id: str = "Xnhyacinth/LongBench"
def _load_datasets(self) -> pd.DataFrame:
"""Load LongBench datasets by individual configs.
-
+
LongBench requires loading each subset as a separate config.
-
+
Returns:
Combined pandas DataFrame with all samples from subsets_to_run.
"""
print(f"Loading LongBench datasets: {self.subsets_to_run}")
dfs = []
-
+
for subset in self.subsets_to_run:
try:
from datasets import load_dataset
- subset_dataset = load_dataset(self.huggingface_dataset_id, subset, split="test")
+
+ subset_dataset = load_dataset(
+ self.huggingface_dataset_id, subset, split="test"
+ )
subset_df = subset_dataset.to_pandas()
subset_df["task"] = subset # Ensure task column exists
dfs.append(subset_df)
@@ -70,12 +98,13 @@ def _load_datasets(self) -> pd.DataFrame:
except Exception as subset_error:
print(f" โ Failed to load {subset}: {str(subset_error)}")
continue
-
+
if not dfs:
raise Exception("No LongBench subsets could be loaded successfully")
-
+
# Combine all subset DataFrames
import pandas as pd
+
combined_df = pd.concat(dfs, ignore_index=True)
print(f"Combined {len(combined_df)} total samples from {len(dfs)} subsets")
return combined_df
@@ -104,35 +133,39 @@ def post_run_evaluate(self, results_df: pd.DataFrame) -> Dict[str, Any]:
task_scores: Dict[str, Any] = {}
standard_scores: List[float] = []
extended_scores: Dict[str, List[float]] = {"0-4k": [], "4-8k": [], "8k+": []}
-
+
for task_name, task_df in task_groups:
try:
if task_name.endswith("_e"):
# Extended dataset - use calculate_metrics_e for length-based breakdown
length_scores: Dict[str, float] = calculate_metrics_e(task_df)
task_scores[task_name] = length_scores
-
+
# Accumulate extended scores for overall calculation
for length_range in extended_scores:
if length_range in length_scores:
- extended_scores[length_range].append(length_scores[length_range])
+ extended_scores[length_range].append(
+ length_scores[length_range]
+ )
else:
# Standard dataset - use calculate_metrics
score: float = calculate_metrics(task_df)
task_scores[task_name] = score
standard_scores.append(score)
-
+
except Exception as e:
print(f"Error evaluating task {task_name}: {str(e)}")
task_scores[task_name] = {"error": str(e)}
# Compute overall metrics
overall_metrics: Dict[str, Any] = {"task_scores": task_scores}
-
+
# Overall score for standard datasets
if standard_scores:
- overall_metrics["standard_overall_score"] = round(sum(standard_scores) / len(standard_scores), 2)
-
+ overall_metrics["standard_overall_score"] = round(
+ sum(standard_scores) / len(standard_scores), 2
+ )
+
# Overall scores for extended datasets by length range
if any(extended_scores.values()):
extended_overall: Dict[str, float] = {}
@@ -141,21 +174,25 @@ def post_run_evaluate(self, results_df: pd.DataFrame) -> Dict[str, Any]:
extended_overall[length_range] = round(sum(scores) / len(scores), 2)
if extended_overall:
overall_metrics["extended_overall_scores"] = extended_overall
-
+
# Compute grand overall score if we have both standard and extended results
all_scores: List[float] = standard_scores.copy()
for scores in extended_scores.values():
all_scores.extend(scores)
-
+
if all_scores:
- overall_metrics["overall_score"] = round(sum(all_scores) / len(all_scores), 2)
-
+ overall_metrics["overall_score"] = round(
+ sum(all_scores) / len(all_scores), 2
+ )
+
# Add summary statistics
overall_metrics["summary"] = {
"total_tasks": len(task_scores),
- "standard_tasks": len([t for t in task_scores.keys() if not t.endswith("_e")]),
+ "standard_tasks": len(
+ [t for t in task_scores.keys() if not t.endswith("_e")]
+ ),
"extended_tasks": len([t for t in task_scores.keys() if t.endswith("_e")]),
- "total_samples": len(results_df)
+ "total_samples": len(results_df),
}
-
- return overall_metrics
\ No newline at end of file
+
+ return overall_metrics
diff --git a/benchmark/longbenchv2/__init__.py b/benchmark/longbenchv2/__init__.py
index b829ae7c..087f8627 100644
--- a/benchmark/longbenchv2/__init__.py
+++ b/benchmark/longbenchv2/__init__.py
@@ -8,4 +8,4 @@
from .calculate_metrics import calculate_metrics
from .longbenchv2 import LongBenchv2
-__all__ = ["calculate_metrics", "LongBenchv2"]
\ No newline at end of file
+__all__ = ["calculate_metrics", "LongBenchv2"]
diff --git a/benchmark/longbenchv2/calculate_metrics.py b/benchmark/longbenchv2/calculate_metrics.py
index f85eebb3..71e91710 100644
--- a/benchmark/longbenchv2/calculate_metrics.py
+++ b/benchmark/longbenchv2/calculate_metrics.py
@@ -29,7 +29,9 @@ def scorer(predictions, answers, lengths, difficulties):
compensated = False
easy, hard, short, medium, long = 0, 0, 0, 0, 0
easy_acc, hard_acc, short_acc, medium_acc, long_acc = 0, 0, 0, 0, 0
- for pred, answer, length, difficulty in zip(predictions, answers, lengths, difficulties):
+ for pred, answer, length, difficulty in zip(
+ predictions, answers, lengths, difficulties
+ ):
acc = int(extract_answer(pred) == answer)
if compensated and pred["pred"] is None:
acc = 0.25 # type:ignore[assignment]
diff --git a/benchmark/longbenchv2/create_huggingface_dataset.py b/benchmark/longbenchv2/create_huggingface_dataset.py
index 95c4b45a..e261342a 100644
--- a/benchmark/longbenchv2/create_huggingface_dataset.py
+++ b/benchmark/longbenchv2/create_huggingface_dataset.py
@@ -31,7 +31,9 @@
# Longbench-v2
for task in ["0shot", "cot"]:
dataset = load_dataset("THUDM/LongBench-v2", split="train")
- dataset = dataset.map(lambda x: {"context": context_prefix[task].format(context=x["context"].strip())})
+ dataset = dataset.map(
+ lambda x: {"context": context_prefix[task].format(context=x["context"].strip())}
+ )
dataset = dataset.map(
lambda x: {
"question": question_template[task].format(
diff --git a/benchmark/longbenchv2/longbenchv2.py b/benchmark/longbenchv2/longbenchv2.py
index 01f56f61..566338f6 100644
--- a/benchmark/longbenchv2/longbenchv2.py
+++ b/benchmark/longbenchv2/longbenchv2.py
@@ -26,29 +26,29 @@ class LongBenchv2(Benchmark):
"""
# All available LongBenchv2 datasets
- all_datasets: List[str] = [
- "0shot",
- "cot"
- ]
-
+ all_datasets: List[str] = ["0shot", "cot"]
+
benchmark_name: str = "longbenchv2"
huggingface_dataset_id: str = "Xnhyacinth/LongBench-v2"
def _load_datasets(self) -> pd.DataFrame:
"""Load LongBenchv2 datasets by individual configs.
-
+
LongBenchv2 requires loading each subset as a separate config.
-
+
Returns:
Combined pandas DataFrame with all samples from subsets_to_run.
"""
print(f"Loading LongBenchv2 datasets: {self.subsets_to_run}")
dfs = []
-
+
for subset in self.subsets_to_run:
try:
from datasets import load_dataset
- subset_dataset = load_dataset(self.huggingface_dataset_id, subset, split="test")
+
+ subset_dataset = load_dataset(
+ self.huggingface_dataset_id, subset, split="test"
+ )
subset_df = subset_dataset.to_pandas()
subset_df["task"] = subset # Ensure task column exists
dfs.append(subset_df)
@@ -56,12 +56,13 @@ def _load_datasets(self) -> pd.DataFrame:
except Exception as subset_error:
print(f" โ Failed to load {subset}: {str(subset_error)}")
continue
-
+
if not dfs:
raise Exception("No LongBenchv2 subsets could be loaded successfully")
-
+
# Combine all subset DataFrames
import pandas as pd
+
combined_df = pd.concat(dfs, ignore_index=True)
print(f"Combined {len(combined_df)} total samples from {len(dfs)} subsets")
return combined_df
@@ -90,20 +91,20 @@ def post_run_evaluate(self, results_df: pd.DataFrame) -> Dict[str, Any]:
task_groups = results_df.groupby("task")
task_scores: Dict[str, Any] = {}
all_scores: List[float] = []
-
+
for task_name, task_df in task_groups:
try:
# Use the calculate_metrics function from HashAttention evaluation
scores = calculate_metrics(task_df)
-
+
# Parse the scores string to extract overall accuracy
if len(scores) >= 2:
- overall_score_str = scores[1].split('\t')[0]
+ overall_score_str = scores[1].split("\t")[0]
try:
overall_score = float(overall_score_str)
task_scores[task_name] = {
"overall_accuracy": overall_score,
- "detailed_scores": scores
+ "detailed_scores": scores,
}
all_scores.append(overall_score)
print(f" โ {task_name}: {overall_score:.1f}%")
@@ -111,21 +112,21 @@ def post_run_evaluate(self, results_df: pd.DataFrame) -> Dict[str, Any]:
task_scores[task_name] = {"error": "Could not parse score"}
else:
task_scores[task_name] = {"error": "No scores returned"}
-
+
except Exception as e:
print(f"Error evaluating task {task_name}: {str(e)}")
task_scores[task_name] = {"error": str(e)}
# Compute overall metrics
overall_score = sum(all_scores) / len(all_scores) if all_scores else 0.0
-
+
overall_metrics: Dict[str, Any] = {
"overall_score": round(overall_score, 1),
"task_scores": task_scores,
"summary": {
"total_tasks": len(task_scores),
- "total_samples": len(results_df)
- }
+ "total_samples": len(results_df),
+ },
}
-
- return overall_metrics
\ No newline at end of file
+
+ return overall_metrics
diff --git a/benchmark/loogle/calculate_metrics.py b/benchmark/loogle/calculate_metrics.py
index 7deb02bd..ab3bdd48 100644
--- a/benchmark/loogle/calculate_metrics.py
+++ b/benchmark/loogle/calculate_metrics.py
@@ -76,7 +76,10 @@ def wrapped_metric(answer, predicted_answer):
try:
return metric_fn(answer, predicted_answer)
except Exception as e:
- print(f"Cannot calculate metric: {e}" f" on answer:{answer} and predicted_answer:{predicted_answer}")
+ print(
+ f"Cannot calculate metric: {e}"
+ f" on answer:{answer} and predicted_answer:{predicted_answer}"
+ )
return {key: 0.0 for key in metric_fn("Hi there", "hi there")}
return wrapped_metric
@@ -93,21 +96,36 @@ def calculate_metrics(df: pd.DataFrame) -> dict:
("exact", get_exact_match),
("partial", get_partial_match),
]:
- match, count = zip(*df_task.apply(lambda x: metric_fn(x["answer"], x["predicted_answer"]), axis=1))
+ match, count = zip(
+ *df_task.apply(
+ lambda x: metric_fn(x["answer"], x["predicted_answer"]), axis=1
+ )
+ )
scores[task][f"{prefix}_match"] = round(sum(match) / sum(count), 4)
else:
- df["predicted_answer"] = df["predicted_answer"].apply(lambda x: x if isinstance(x, str) else "")
+ df["predicted_answer"] = df["predicted_answer"].apply(
+ lambda x: x if isinstance(x, str) else ""
+ )
for metric_fn in [get_bleu_score, get_rouge_score, get_meteor_score]: # type: ignore
metric_fn = try_except_metric(metric_fn)
- metric_scores = [metric_fn(row["answer"], row["predicted_answer"]) for _, row in df_task.iterrows()]
+ metric_scores = [
+ metric_fn(row["answer"], row["predicted_answer"])
+ for _, row in df_task.iterrows()
+ ]
scores[task].update(pd.DataFrame(metric_scores).mean().to_dict())
# BERT scores (batched)
scores[task]["bert"] = (
- score(df_task["answer"].to_list(), df_task["predicted_answer"].to_list(), lang="EN")[1].mean().item()
+ score(
+ df_task["answer"].to_list(),
+ df_task["predicted_answer"].to_list(),
+ lang="EN",
+ )[1]
+ .mean()
+ .item()
)
return scores
diff --git a/benchmark/loogle/create_huggingface_dataset.py b/benchmark/loogle/create_huggingface_dataset.py
index ef2fd15b..78eb732e 100644
--- a/benchmark/loogle/create_huggingface_dataset.py
+++ b/benchmark/loogle/create_huggingface_dataset.py
@@ -30,19 +30,30 @@
}
# Source: https://github.com/bigai-nlco/LooGLE/blob/main/config/task2maxlen.json
-max_new_tokens = {"shortdep_qa": 300, "longdep_qa": 500, "longdep_summarization": 500, "shortdep_cloze": 50}
+max_new_tokens = {
+ "shortdep_qa": 300,
+ "longdep_qa": 500,
+ "longdep_summarization": 500,
+ "shortdep_cloze": 50,
+}
for task in ["shortdep_qa", "longdep_qa", "shortdep_cloze", "longdep_summarization"]:
- df = load_dataset("bigainlco/LooGLE", task, split="test", trust_remote_code=True).to_pandas()
+ df = load_dataset(
+ "bigainlco/LooGLE", task, split="test", trust_remote_code=True
+ ).to_pandas()
if task == "longdep_summarization":
df["question"] = ""
df = df.rename(columns={"output": "answer", "input": "context"})
else:
- df["qa_pairs"] = df["qa_pairs"].apply(lambda x: eval(x) if x != "none" else [{"Q": "", "A": "", "S": [""]}])
+ df["qa_pairs"] = df["qa_pairs"].apply(
+ lambda x: eval(x) if x != "none" else [{"Q": "", "A": "", "S": [""]}]
+ )
df = df.explode("qa_pairs")
- df = pd.concat([df.drop(["qa_pairs"], axis=1), df["qa_pairs"].apply(pd.Series)], axis=1)
+ df = pd.concat(
+ [df.drop(["qa_pairs"], axis=1), df["qa_pairs"].apply(pd.Series)], axis=1
+ )
df = df.rename(columns={"A": "answer", "Q": "question", "input": "context"})
if task == "shortdep_cloze":
df["answer"] = df["answer"].apply(json.dumps, ensure_ascii=False)
diff --git a/benchmark/loogle/loogle.py b/benchmark/loogle/loogle.py
index d66b77ba..9c311a9b 100644
--- a/benchmark/loogle/loogle.py
+++ b/benchmark/loogle/loogle.py
@@ -31,27 +31,30 @@ class Loogle(Benchmark):
"shortdep_qa",
"longdep_qa",
"shortdep_cloze",
- "longdep_summarization"
+ "longdep_summarization",
]
-
+
benchmark_name: str = "loogle"
huggingface_dataset_id: str = "simonjegou/loogle"
def _load_datasets(self) -> pd.DataFrame:
"""Load Loogle datasets by individual configs.
-
+
Loogle requires loading each subset as a separate config.
-
+
Returns:
Combined pandas DataFrame with all samples from subsets_to_run.
"""
print(f"Loading Loogle datasets: {self.subsets_to_run}")
dfs = []
-
+
for subset in self.subsets_to_run:
try:
from datasets import load_dataset
- subset_dataset = load_dataset(self.huggingface_dataset_id, subset, split="test")
+
+ subset_dataset = load_dataset(
+ self.huggingface_dataset_id, subset, split="test"
+ )
subset_df = subset_dataset.to_pandas()
subset_df["task"] = subset # Ensure task column exists
dfs.append(subset_df)
@@ -59,12 +62,13 @@ def _load_datasets(self) -> pd.DataFrame:
except Exception as subset_error:
print(f" โ Failed to load {subset}: {str(subset_error)}")
continue
-
+
if not dfs:
raise Exception("No Loogle subsets could be loaded successfully")
-
+
# Combine all subset DataFrames
import pandas as pd
+
combined_df = pd.concat(dfs, ignore_index=True)
print(f"Combined {len(combined_df)} total samples from {len(dfs)} subsets")
return combined_df
@@ -88,7 +92,7 @@ def post_run_evaluate(self, results_df: pd.DataFrame) -> Dict[str, Any]:
# Use the calculate_metrics function from HashAttention evaluation
task_scores: Dict[str, Dict[str, float]] = calculate_metrics(results_df)
-
+
# Compute overall score by averaging task scores
all_scores: List[float] = []
for task, scores in task_scores.items():
@@ -101,16 +105,16 @@ def post_run_evaluate(self, results_df: pd.DataFrame) -> Dict[str, Any]:
# Fallback to first available metric
elif scores:
all_scores.append(list(scores.values())[0])
-
+
overall_score = sum(all_scores) / len(all_scores) if all_scores else 0.0
-
+
overall_metrics: Dict[str, Any] = {
"overall_score": round(overall_score, 4),
"task_scores": task_scores,
"summary": {
"total_tasks": len(task_scores),
- "total_samples": len(results_df)
- }
+ "total_samples": len(results_df),
+ },
}
-
- return overall_metrics
\ No newline at end of file
+
+ return overall_metrics
diff --git a/benchmark/mock_benchmark/__init__.py b/benchmark/mock_benchmark/__init__.py
index 0774219f..09d3ed56 100644
--- a/benchmark/mock_benchmark/__init__.py
+++ b/benchmark/mock_benchmark/__init__.py
@@ -1,9 +1,7 @@
-
-
"""
Mock benchmark module for testing and demonstration purposes.
"""
from .mock_benchmark import MockBenchmark
-__all__ = ["MockBenchmark"]
\ No newline at end of file
+__all__ = ["MockBenchmark"]
diff --git a/benchmark/mock_benchmark/mock_benchmark.py b/benchmark/mock_benchmark/mock_benchmark.py
index a069b919..2f0108d0 100644
--- a/benchmark/mock_benchmark/mock_benchmark.py
+++ b/benchmark/mock_benchmark/mock_benchmark.py
@@ -27,11 +27,13 @@ class MockBenchmark(Benchmark):
# Class attributes required by base Benchmark class
all_datasets: List[str] = ["reading_comprehension"]
benchmark_name: str = "mock_benchmark"
- huggingface_dataset_id: str = "mock/dataset" # Not actually used since we override _load_datasets
+ huggingface_dataset_id: str = (
+ "mock/dataset" # Not actually used since we override _load_datasets
+ )
def _load_datasets(self) -> pd.DataFrame:
"""Load mock dataset with hardcoded samples.
-
+
Returns:
DataFrame containing 5 mock samples with context, question, and answers.
"""
@@ -51,7 +53,6 @@ def _load_datasets(self) -> pd.DataFrame:
in their leaves, where specialized organelles called chloroplasts contain the
chlorophyll necessary for the process.
""".strip(),
-
"history_context": """
The Renaissance was a period of cultural, artistic, and intellectual revival
that began in Italy during the 14th century and spread throughout Europe.
@@ -67,7 +68,6 @@ def _load_datasets(self) -> pd.DataFrame:
laid the foundation for the Scientific Revolution and the Enlightenment that
would follow.
""".strip(),
-
"geography_context": """
The Amazon rainforest, often called the "lungs of the Earth," is the world's
largest tropical rainforest. Located primarily in Brazil, it also extends
@@ -81,7 +81,7 @@ def _load_datasets(self) -> pd.DataFrame:
drainage basin. Unfortunately, the rainforest faces threats from deforestation,
mining, and climate change, making its conservation critical for the health
of our planet.
- """.strip()
+ """.strip(),
}
# Create sample data - 5 samples total, 2 sharing the same context
@@ -89,46 +89,50 @@ def _load_datasets(self) -> pd.DataFrame:
{
"context": contexts["science_context"],
"question": "What are the two main stages of photosynthesis?",
- "answers": ["light-dependent reactions and light-independent reactions",
- "light-dependent reactions and Calvin cycle"],
- "task": "reading_comprehension"
+ "answers": [
+ "light-dependent reactions and light-independent reactions",
+ "light-dependent reactions and Calvin cycle",
+ ],
+ "task": "reading_comprehension",
},
{
"context": contexts["science_context"], # Same context as sample 1
"question": "What gas is produced as a byproduct of photosynthesis?",
"answers": ["oxygen"],
- "task": "reading_comprehension"
+ "task": "reading_comprehension",
},
{
"context": contexts["history_context"],
"question": "In which century did the Renaissance begin?",
"answers": ["14th century", "14th"],
- "task": "reading_comprehension"
+ "task": "reading_comprehension",
},
{
"context": contexts["geography_context"],
"question": "Why is the Amazon rainforest called the 'lungs of the Earth'?",
- "answers": ["because it absorbs carbon dioxide and produces oxygen",
- "it regulates global climate by absorbing CO2 and producing oxygen"],
- "task": "reading_comprehension"
+ "answers": [
+ "because it absorbs carbon dioxide and produces oxygen",
+ "it regulates global climate by absorbing CO2 and producing oxygen",
+ ],
+ "task": "reading_comprehension",
},
{
"context": contexts["geography_context"],
"question": "Which river flows through the Amazon rainforest?",
"answers": ["Amazon River", "the Amazon River"],
- "task": "reading_comprehension"
- }
+ "task": "reading_comprehension",
+ },
]
# Convert to DataFrame
df: pd.DataFrame = pd.DataFrame(sample_data)
-
+
# Add sample IDs for tracking
df["sample_id"] = range(1, len(df) + 1)
-
+
print(f"Loaded {len(df)} mock samples")
print(f"Unique contexts: {df['context'].nunique()}")
-
+
return df
def post_run_evaluate(self, results_df: pd.DataFrame) -> Dict[str, Any]:
@@ -159,32 +163,38 @@ def post_run_evaluate(self, results_df: pd.DataFrame) -> Dict[str, Any]:
predicted_answer: str = str(row["predicted_answer"]).strip().lower()
ground_truth_answers: List[str] = row["answers"]
sample_id: int = row["sample_id"]
-
+
# Check if prediction matches any ground truth answer
is_correct: bool = False
for gt_answer in ground_truth_answers:
gt_answer_normalized: str = str(gt_answer).strip().lower()
-
+
# Check exact match or substring match
- if (predicted_answer == gt_answer_normalized or
- gt_answer_normalized in predicted_answer):
+ if (
+ predicted_answer == gt_answer_normalized
+ or gt_answer_normalized in predicted_answer
+ ):
is_correct = True
break
-
+
if is_correct:
correct_predictions += 1
-
- sample_scores.append({
- "sample_id": sample_id,
- "question": row["question"],
- "predicted_answer": row["predicted_answer"],
- "ground_truth": ground_truth_answers,
- "correct": is_correct
- })
+
+ sample_scores.append(
+ {
+ "sample_id": sample_id,
+ "question": row["question"],
+ "predicted_answer": row["predicted_answer"],
+ "ground_truth": ground_truth_answers,
+ "correct": is_correct,
+ }
+ )
# Calculate metrics
- accuracy: float = correct_predictions / total_samples if total_samples > 0 else 0.0
-
+ accuracy: float = (
+ correct_predictions / total_samples if total_samples > 0 else 0.0
+ )
+
metrics: Dict[str, Any] = {
"accuracy": round(accuracy, 3),
"correct_predictions": correct_predictions,
@@ -194,8 +204,8 @@ def post_run_evaluate(self, results_df: pd.DataFrame) -> Dict[str, Any]:
"benchmark": self.benchmark_name,
"task": "reading_comprehension",
"unique_contexts": results_df["context"].nunique(),
- "evaluation_method": "exact_match_and_substring"
- }
+ "evaluation_method": "exact_match_and_substring",
+ },
}
- return metrics
\ No newline at end of file
+ return metrics
diff --git a/benchmark/ruler/calculate_metrics.py b/benchmark/ruler/calculate_metrics.py
index 16d1b924..4fa78ec9 100644
--- a/benchmark/ruler/calculate_metrics.py
+++ b/benchmark/ruler/calculate_metrics.py
@@ -9,7 +9,12 @@
def string_match_part(preds, refs):
score = (
- sum([max([1.0 if r.lower() in pred.lower() else 0.0 for r in ref]) for pred, ref in zip(preds, refs)])
+ sum(
+ [
+ max([1.0 if r.lower() in pred.lower() else 0.0 for r in ref])
+ for pred, ref in zip(preds, refs)
+ ]
+ )
/ len(preds)
* 100
)
@@ -19,7 +24,10 @@ def string_match_part(preds, refs):
def string_match_all(preds, refs):
score = (
sum(
- [sum([1.0 if r.lower() in pred.lower() else 0.0 for r in ref]) / len(ref) for pred, ref in zip(preds, refs)]
+ [
+ sum([1.0 if r.lower() in pred.lower() else 0.0 for r in ref]) / len(ref)
+ for pred, ref in zip(preds, refs)
+ ]
)
/ len(preds)
* 100
@@ -31,7 +39,9 @@ def calculate_metrics(df: pd.DataFrame) -> dict:
scores = {}
np_pattern = re.compile(r"[\x00-\x1f]")
- df["predicted_answer"] = df["predicted_answer"].apply(lambda x: np_pattern.sub("", x.strip()).strip())
+ df["predicted_answer"] = df["predicted_answer"].apply(
+ lambda x: np_pattern.sub("", x.strip()).strip()
+ )
for task, df_task in df.groupby("task"):
task_category = task.split("_")[0]
diff --git a/benchmark/ruler/create_huggingface_dataset.py b/benchmark/ruler/create_huggingface_dataset.py
index b31f14bf..74628967 100644
--- a/benchmark/ruler/create_huggingface_dataset.py
+++ b/benchmark/ruler/create_huggingface_dataset.py
@@ -13,7 +13,9 @@
QUESTION_PATTERNS = {
"niah": re.compile(r"What (?:is|are all) the special magic"),
"vt": re.compile(r"Question: Find all variables that are assigned the value"),
- "cwe": re.compile(r"Question: What are the 10 most common words in the above list\?"),
+ "cwe": re.compile(
+ r"Question: What are the 10 most common words in the above list\?"
+ ),
"fwe": re.compile(r"Question: Do not provide any explanation\."),
"qa": re.compile(r"Answer the question based on the given documents\."),
}
@@ -41,7 +43,9 @@ def get_dataframe(path):
Parse the data from the provided path and return a DataFrame with the context, question, answers and task
"""
- assert re.match(r".*\/\d+$", str(path)), "The path should must ends with the context length (e.g. with /4096)"
+ assert re.match(
+ r".*\/\d+$", str(path)
+ ), "The path should must ends with the context length (e.g. with /4096)"
df_list = []
for task_path in Path(path).glob("**/*.jsonl"):
@@ -60,21 +64,27 @@ def split_context_question(text):
question, answer = qa[:idx], qa[idx:]
return context, question, answer
- df["context"], df["question"], df["answer_prefix"] = zip(*df["input"].apply(split_context_question))
+ df["context"], df["question"], df["answer_prefix"] = zip(
+ *df["input"].apply(split_context_question)
+ )
df["task"] = task
df["max_new_tokens"] = MAX_NEW_TOKENS[task.split("_")[0]]
df_list.append(df)
# Concatenate all the dataframes
df = pd.concat(df_list)
- df = df[["context", "question", "answer_prefix", "outputs", "task", "max_new_tokens"]]
+ df = df[
+ ["context", "question", "answer_prefix", "outputs", "task", "max_new_tokens"]
+ ]
df = df.rename(columns={"outputs": "answer"}).reset_index(drop=True)
return df
if __name__ == "__main__":
- data_dir = Path("/mnt/workspace/projects/RULER/scripts/data/data/") # output of the generate.sh script
+ data_dir = Path(
+ "/mnt/workspace/projects/RULER/scripts/data/data/"
+ ) # output of the generate.sh script
repo_id = "simonjegou/ruler"
# Loop over all the context lengths
diff --git a/benchmark/ruler/ruler.py b/benchmark/ruler/ruler.py
index afe770d5..1035c666 100644
--- a/benchmark/ruler/ruler.py
+++ b/benchmark/ruler/ruler.py
@@ -27,31 +27,29 @@ class Ruler(Benchmark):
"""
# All available Ruler datasets (context lengths)
- all_datasets: List[str] = [
- "4096",
- "8192",
- "16384",
- "32768"
- ]
-
+ all_datasets: List[str] = ["4096", "8192", "16384", "32768"]
+
benchmark_name: str = "ruler"
huggingface_dataset_id: str = "simonjegou/ruler"
def _load_datasets(self) -> pd.DataFrame:
"""Load Ruler datasets by individual configs.
-
+
Ruler requires loading each context length as a separate config.
-
+
Returns:
Combined pandas DataFrame with all samples from subsets_to_run.
"""
print(f"Loading Ruler datasets: {self.subsets_to_run}")
dfs = []
-
+
for subset in self.subsets_to_run:
try:
from datasets import load_dataset
- subset_dataset = load_dataset(self.huggingface_dataset_id, subset, split="test")
+
+ subset_dataset = load_dataset(
+ self.huggingface_dataset_id, subset, split="test"
+ )
subset_df = subset_dataset.to_pandas()
# Add context length as a column for analysis
subset_df["context_length"] = subset
@@ -60,12 +58,13 @@ def _load_datasets(self) -> pd.DataFrame:
except Exception as subset_error:
print(f" โ Failed to load {subset}: {str(subset_error)}")
continue
-
+
if not dfs:
raise Exception("No Ruler subsets could be loaded successfully")
-
+
# Combine all subset DataFrames
import pandas as pd
+
combined_df = pd.concat(dfs, ignore_index=True)
print(f"Combined {len(combined_df)} total samples from {len(dfs)} subsets")
return combined_df
@@ -91,15 +90,15 @@ def post_run_evaluate(self, results_df: pd.DataFrame) -> Dict[str, Any]:
# Use the calculate_metrics function from HashAttention evaluation
task_scores: Dict[str, Dict[str, float]] = calculate_metrics(results_df)
-
+
# Extract string_match scores and compute overall
all_scores: List[float] = []
for task, scores in task_scores.items():
if "string_match" in scores:
all_scores.append(scores["string_match"])
-
+
overall_score = sum(all_scores) / len(all_scores) if all_scores else 0.0
-
+
# Group by context length if available
context_length_scores: Dict[str, float] = {}
if "context_length" in results_df.columns:
@@ -107,9 +106,19 @@ def post_run_evaluate(self, results_df: pd.DataFrame) -> Dict[str, Any]:
length_df = results_df[results_df["context_length"] == context_length]
if len(length_df) > 0:
length_scores = calculate_metrics(length_df)
- length_overall = sum(score.get("string_match", 0) for score in length_scores.values()) / len(length_scores) if length_scores else 0.0
- context_length_scores[str(context_length)] = round(length_overall, 2)
-
+ length_overall = (
+ sum(
+ score.get("string_match", 0)
+ for score in length_scores.values()
+ )
+ / len(length_scores)
+ if length_scores
+ else 0.0
+ )
+ context_length_scores[str(context_length)] = round(
+ length_overall, 2
+ )
+
overall_metrics: Dict[str, Any] = {
"overall_score": round(overall_score, 2),
"task_scores": task_scores,
@@ -117,8 +126,8 @@ def post_run_evaluate(self, results_df: pd.DataFrame) -> Dict[str, Any]:
"summary": {
"total_tasks": len(task_scores),
"total_samples": len(results_df),
- "context_lengths": list(context_length_scores.keys())
- }
+ "context_lengths": list(context_length_scores.keys()),
+ },
}
-
- return overall_metrics
\ No newline at end of file
+
+ return overall_metrics
diff --git a/benchmark/scripts/benchmark.py b/benchmark/scripts/benchmark.py
index 289fa0f1..e69e0802 100644
--- a/benchmark/scripts/benchmark.py
+++ b/benchmark/scripts/benchmark.py
@@ -19,9 +19,12 @@
from benchmark.executor import BenchmarkExecutor
from benchmark.executor_config import BenchmarkConfig, AdapterConfig
-from sparse_attention_hub.sparse_attention.research_attention import ResearchAttentionConfig
+from sparse_attention_hub.sparse_attention.research_attention import (
+ ResearchAttentionConfig,
+)
from sparse_attention_hub.sparse_attention.research_attention.maskers.fixed.implementations import (
- LocalMaskerConfig, SinkMaskerConfig
+ LocalMaskerConfig,
+ SinkMaskerConfig,
)
# ============================================================================
@@ -29,80 +32,63 @@
# ============================================================================
# GPU Configuration
-GPUS = [0,2,7] # Use all available GPUs
+GPUS = [0, 2, 7] # Use all available GPUs
MAX_CONCURRENT_RUNS = 3 # One per GPU
# Model List
MODELS = [
- "microsoft/Phi-4-mini-instruct",
- "meta-llama/Llama-3.2-1B-Instruct",
+ "microsoft/Phi-4-mini-instruct",
+ "meta-llama/Llama-3.2-1B-Instruct",
]
# Sparse Attention Configurations
SPARSE_CONFIGS = [
# Dense baseline (no sparse attention)
("dense", None),
-
# StreamingLLM configurations
- ("streaming_conservative", ResearchAttentionConfig(masker_configs=[
- SinkMaskerConfig(sink_size=4),
- LocalMaskerConfig(window_size=16)
- ])),
+ (
+ "streaming_conservative",
+ ResearchAttentionConfig(
+ masker_configs=[
+ SinkMaskerConfig(sink_size=4),
+ LocalMaskerConfig(window_size=16),
+ ]
+ ),
+ ),
]
# Benchmark List
# 1. InfiniteBench - using passkey task
infinite_bench_config = BenchmarkConfig(
- benchmark_name="infinite_bench",
- subsets=["passkey"]
+ benchmark_name="infinite_bench", subsets=["passkey"]
)
# 2. Ruler - using 4096 context length
-ruler_config = BenchmarkConfig(
- benchmark_name="ruler",
- subsets=["4096"]
-)
+ruler_config = BenchmarkConfig(benchmark_name="ruler", subsets=["4096"])
# 3. Loogle - using shortdep_qa task
-loogle_config = BenchmarkConfig(
- benchmark_name="loogle",
- subsets=["shortdep_qa"]
-)
+loogle_config = BenchmarkConfig(benchmark_name="loogle", subsets=["shortdep_qa"])
# 4. ZeroScrolls - using gov_report task
zero_scrolls_config = BenchmarkConfig(
- benchmark_name="zero_scrolls",
- subsets=["default"]
+ benchmark_name="zero_scrolls", subsets=["default"]
)
# 5. LongBenchv2 - using 0shot task
-longbenchv2_config = BenchmarkConfig(
- benchmark_name="longbenchv2",
- subsets=["0shot"]
-)
+longbenchv2_config = BenchmarkConfig(benchmark_name="longbenchv2", subsets=["0shot"])
# 6. AIME2024 - using single task
-aime2024_config = BenchmarkConfig(
- benchmark_name="aime2024",
- subsets=["aime2024"]
-)
+aime2024_config = BenchmarkConfig(benchmark_name="aime2024", subsets=["aime2024"])
# 7. AIME2025 - using single task
-aime2025_config = BenchmarkConfig(
- benchmark_name="aime2025",
- subsets=["aime2025"]
-)
+aime2025_config = BenchmarkConfig(benchmark_name="aime2025", subsets=["aime2025"])
# 8. LongBench (existing) - using narrativeqa task
-longbench_config = BenchmarkConfig(
- benchmark_name="longbench",
- subsets=["narrativeqa"]
-)
+longbench_config = BenchmarkConfig(benchmark_name="longbench", subsets=["narrativeqa"])
# 9. Mock Benchmark (existing) - using single task
mock_benchmark_config = BenchmarkConfig(
- benchmark_name="mock_benchmark",
- subsets=["reading_comprehension"]
+ benchmark_name="mock_benchmark", subsets=["reading_comprehension"]
)
# List of all sample configurations
@@ -115,7 +101,7 @@
aime2024_config,
aime2025_config,
longbench_config,
- mock_benchmark_config
+ mock_benchmark_config,
]
@@ -127,7 +113,7 @@
},
tokenizer_kwargs={
"padding_side": "left",
- }
+ },
)
# Generation Parameters
@@ -157,7 +143,7 @@
if __name__ == "__main__":
print("๐ Starting Minimalistic Benchmark Suite")
print("=" * 50)
-
+
print(f"๐ง Configuration:")
print(f" - GPUs: {GPUS}")
print(f" - Models: {len(MODELS)}")
@@ -174,25 +160,29 @@
print(f" - Benchmarks: {len(BENCHMARKS)}")
for i, benchmark in enumerate(BENCHMARKS, 1):
if benchmark.subsets:
- print(f" {i}. {benchmark.benchmark_name}: {len(benchmark.subsets)} subsets")
+ print(
+ f" {i}. {benchmark.benchmark_name}: {len(benchmark.subsets)} subsets"
+ )
else:
print(f" {i}. {benchmark.benchmark_name}: all subsets")
print(f" - Max concurrent: {MAX_CONCURRENT_RUNS}")
print(f" - Result dir: {RESULT_DIR}")
print(f" - Resumability: {'enabled' if ENABLE_RESUMABILITY else 'disabled'}")
-
+
# Calculate total combinations
total_models = len(MODELS)
total_configs = len(SPARSE_CONFIGS)
total_benchmarks = sum(len(b.subsets) if b.subsets else 1 for b in BENCHMARKS)
total_combinations = total_models * total_configs * total_benchmarks
-
+
print(f"\n๐ Experiment Matrix: {total_combinations} total combinations")
print(f" - Models: {total_models}")
print(f" - Sparse configs: {total_configs}")
print(f" - Benchmark-subsets: {total_benchmarks}")
- print(f" - Estimated time: {total_combinations * TIMEOUT_PER_BENCHMARK / 3600:.1f} hours (worst case)")
-
+ print(
+ f" - Estimated time: {total_combinations * TIMEOUT_PER_BENCHMARK / 3600:.1f} hours (worst case)"
+ )
+
# Create executor
print(f"\n๐ง Initializing BenchmarkExecutor...")
executor = BenchmarkExecutor(
@@ -202,9 +192,9 @@
enable_resumability=ENABLE_RESUMABILITY,
required_result_files=["raw_results.csv"],
timeout_per_benchmark=TIMEOUT_PER_BENCHMARK,
- verbose=True
+ verbose=True,
)
-
+
# Run benchmarks
print(f"\n๐ฏ Running Benchmark Matrix...")
try:
@@ -214,9 +204,9 @@
benchmark_configs=BENCHMARKS,
adapter_config=ADAPTER_CONFIG,
generation_kwargs=GENERATION_KWARGS,
- request_kwargs=REQUEST_KWARGS
+ request_kwargs=REQUEST_KWARGS,
)
-
+
# Print summary
print(f"\nโ
Benchmark Execution Completed!")
print(f" - Total: {results.progress.total_stubs}")
@@ -224,10 +214,10 @@
print(f" - Failed: {results.progress.failed_stubs}")
print(f" - Skipped: {results.progress.skipped_stubs}")
print(f" - Results saved to: {RESULT_DIR}")
-
+
except KeyboardInterrupt:
print(f"\nโ ๏ธ Interrupted by user")
print(f" Partial results in: {RESULT_DIR}")
except Exception as e:
print(f"\nโ Execution failed: {e}")
- raise
+ raise
diff --git a/benchmark/scripts/executor_example.py b/benchmark/scripts/executor_example.py
index 092e4467..835581e9 100644
--- a/benchmark/scripts/executor_example.py
+++ b/benchmark/scripts/executor_example.py
@@ -20,12 +20,19 @@
from benchmark.executor import BenchmarkExecutor
from benchmark.executor_config import BenchmarkConfig, AdapterConfig
-from sparse_attention_hub.sparse_attention.research_attention import ResearchAttentionConfig
+from sparse_attention_hub.sparse_attention.research_attention import (
+ ResearchAttentionConfig,
+)
from sparse_attention_hub.sparse_attention.research_attention.maskers.fixed.implementations import (
- LocalMaskerConfig, SinkMaskerConfig, OracleTopKConfig, OracleTopPMaskerConfig
+ LocalMaskerConfig,
+ SinkMaskerConfig,
+ OracleTopKConfig,
+ OracleTopPMaskerConfig,
)
from sparse_attention_hub.sparse_attention.research_attention.maskers.sampling.implementations import (
- AdaptiveSamplingMaskerConfig, RandomSamplingMaskerConfig, MagicPigConfig
+ AdaptiveSamplingMaskerConfig,
+ RandomSamplingMaskerConfig,
+ MagicPigConfig,
)
# ============================================================================
@@ -40,121 +47,140 @@
# Model List
MODELS = [
- "meta-llama/Llama-3.1-8B-Instruct",
+ "meta-llama/Llama-3.1-8B-Instruct",
]
# Sparse Attention Configurations
SPARSE_CONFIGS = [
# Dense baseline (no sparse attention)
("dense", None),
-
# StreamingLLM configurations
- ("streaming_conservative", ResearchAttentionConfig(masker_configs=[
- SinkMaskerConfig(sink_size=128),
- LocalMaskerConfig(window_size=INTENDED_SPARSITY)
- ])),
- #Oracle-TopK
- ("streaming_oracle_topk", ResearchAttentionConfig(masker_configs=[
- SinkMaskerConfig(sink_size=128),
- LocalMaskerConfig(window_size=128),
- OracleTopKConfig(heavy_size=INTENDED_SPARSITY)
- ])),
+ (
+ "streaming_conservative",
+ ResearchAttentionConfig(
+ masker_configs=[
+ SinkMaskerConfig(sink_size=128),
+ LocalMaskerConfig(window_size=INTENDED_SPARSITY),
+ ]
+ ),
+ ),
+ # Oracle-TopK
+ (
+ "streaming_oracle_topk",
+ ResearchAttentionConfig(
+ masker_configs=[
+ SinkMaskerConfig(sink_size=128),
+ LocalMaskerConfig(window_size=128),
+ OracleTopKConfig(heavy_size=INTENDED_SPARSITY),
+ ]
+ ),
+ ),
# Oracle-TopP
- ("streaming_oracle_topp", ResearchAttentionConfig(masker_configs=[
- SinkMaskerConfig(sink_size=128),
- LocalMaskerConfig(window_size=128),
- OracleTopPMaskerConfig(top_p=0.85)
- ])),
+ (
+ "streaming_oracle_topp",
+ ResearchAttentionConfig(
+ masker_configs=[
+ SinkMaskerConfig(sink_size=128),
+ LocalMaskerConfig(window_size=128),
+ OracleTopPMaskerConfig(top_p=0.85),
+ ]
+ ),
+ ),
# Adaptive Sampling
- ("streaming_adaptive_sampling", ResearchAttentionConfig(masker_configs=[
- SinkMaskerConfig(sink_size=128),
- LocalMaskerConfig(window_size=128),
- OracleTopKConfig(heavy_size=128),
- AdaptiveSamplingMaskerConfig(base_rate_sampling=0.05, epsilon=0.25, delta=0.25, init_offset=128, local_offset=128)
- ])),
- # Random Sampling
- ("streaming_random_sampling", ResearchAttentionConfig(masker_configs=[
- SinkMaskerConfig(sink_size=128),
- LocalMaskerConfig(window_size=128),
- RandomSamplingMaskerConfig(sampling_rate=0.1)
- ])),
- # MagicPig
- ("streaming_magicpig", ResearchAttentionConfig(masker_configs=[
- SinkMaskerConfig(sink_size=128),
- LocalMaskerConfig(window_size=128),
- MagicPigConfig(lsh_l=8, lsh_k=8)
- ])),
+ (
+ "streaming_adaptive_sampling",
+ ResearchAttentionConfig(
+ masker_configs=[
+ SinkMaskerConfig(sink_size=128),
+ LocalMaskerConfig(window_size=128),
+ OracleTopKConfig(heavy_size=128),
+ AdaptiveSamplingMaskerConfig(
+ base_rate_sampling=0.05,
+ epsilon=0.25,
+ delta=0.25,
+ init_offset=128,
+ local_offset=128,
+ ),
+ ]
+ ),
+ ),
+ # Random Sampling
+ (
+ "streaming_random_sampling",
+ ResearchAttentionConfig(
+ masker_configs=[
+ SinkMaskerConfig(sink_size=128),
+ LocalMaskerConfig(window_size=128),
+ RandomSamplingMaskerConfig(sampling_rate=0.1),
+ ]
+ ),
+ ),
+ # MagicPig
+ (
+ "streaming_magicpig",
+ ResearchAttentionConfig(
+ masker_configs=[
+ SinkMaskerConfig(sink_size=128),
+ LocalMaskerConfig(window_size=128),
+ MagicPigConfig(lsh_l=8, lsh_k=8),
+ ]
+ ),
+ ),
]
# Benchmark List
# 1. InfiniteBench - using passkey task
infinite_bench_config = BenchmarkConfig(
- benchmark_name="infinite_bench",
- subsets=["passkey"]
+ benchmark_name="infinite_bench", subsets=["passkey"]
)
# 2. Ruler - using 4096 context length
-ruler_config = BenchmarkConfig(
- benchmark_name="ruler",
- subsets=["4096"]
-)
+ruler_config = BenchmarkConfig(benchmark_name="ruler", subsets=["4096"])
# 3. Loogle - using shortdep_qa task
loogle_config = BenchmarkConfig(
benchmark_name="loogle",
subsets=["shortdep_qa"],
- #subsets=["longdep_qa"],
- #subsets=["shortdep_cloze"],
- #subsets=["longdep_summarization"],
+ # subsets=["longdep_qa"],
+ # subsets=["shortdep_cloze"],
+ # subsets=["longdep_summarization"],
)
# 4. ZeroScrolls - using gov_report task
zero_scrolls_config = BenchmarkConfig(
- benchmark_name="zero_scrolls",
- subsets=["default"]
+ benchmark_name="zero_scrolls", subsets=["default"]
)
# 5. LongBenchv2 - using 0shot task
-longbenchv2_config = BenchmarkConfig(
- benchmark_name="longbenchv2",
- subsets=["0shot"]
-)
+longbenchv2_config = BenchmarkConfig(benchmark_name="longbenchv2", subsets=["0shot"])
# 6. AIME2024 - using single task
-aime2024_config = BenchmarkConfig(
- benchmark_name="aime2024",
- subsets=["aime2024"]
-)
+aime2024_config = BenchmarkConfig(benchmark_name="aime2024", subsets=["aime2024"])
# 7. AIME2025 - using single task
-aime2025_config = BenchmarkConfig(
- benchmark_name="aime2025",
- subsets=["aime2025"]
-)
+aime2025_config = BenchmarkConfig(benchmark_name="aime2025", subsets=["aime2025"])
# 8. LongBench (existing) - using narrativeqa task
longbench_config = BenchmarkConfig(
- benchmark_name="longbench",
- subsets=["passage_retrieval_en"]
+ benchmark_name="longbench", subsets=["passage_retrieval_en"]
)
# 9. Mock Benchmark (existing) - using single task
mock_benchmark_config = BenchmarkConfig(
- benchmark_name="mock_benchmark",
- subsets=["reading_comprehension"]
+ benchmark_name="mock_benchmark", subsets=["reading_comprehension"]
)
# List of all sample configurations
BENCHMARKS = [
- #infinite_bench_config,
- #ruler_config,
+ # infinite_bench_config,
+ # ruler_config,
loogle_config,
- #zero_scrolls_config,
- #longbenchv2_config,
- #aime2024_config,
- #aime2025_config,
- #longbench_config,
- #mock_benchmark_config
+ # zero_scrolls_config,
+ # longbenchv2_config,
+ # aime2024_config,
+ # aime2025_config,
+ # longbench_config,
+ # mock_benchmark_config
]
@@ -167,7 +193,7 @@
},
tokenizer_kwargs={
"padding_side": "left",
- }
+ },
)
# Generation Parameters
@@ -196,7 +222,7 @@
if __name__ == "__main__":
print("๐ Starting Minimalistic Benchmark Suite")
print("=" * 50)
-
+
print(f"๐ง Configuration:")
print(f" - GPUs: {GPUS}")
print(f" - Models: {len(MODELS)}")
@@ -213,25 +239,29 @@
print(f" - Benchmarks: {len(BENCHMARKS)}")
for i, benchmark in enumerate(BENCHMARKS, 1):
if benchmark.subsets:
- print(f" {i}. {benchmark.benchmark_name}: {len(benchmark.subsets)} subsets")
+ print(
+ f" {i}. {benchmark.benchmark_name}: {len(benchmark.subsets)} subsets"
+ )
else:
print(f" {i}. {benchmark.benchmark_name}: all subsets")
print(f" - Max concurrent: {MAX_CONCURRENT_RUNS}")
print(f" - Result dir: {RESULT_DIR}")
print(f" - Resumability: {'enabled' if ENABLE_RESUMABILITY else 'disabled'}")
-
+
# Calculate total combinations
total_models = len(MODELS)
total_configs = len(SPARSE_CONFIGS)
total_benchmarks = sum(len(b.subsets) if b.subsets else 1 for b in BENCHMARKS)
total_combinations = total_models * total_configs * total_benchmarks
-
+
print(f"\n๐ Experiment Matrix: {total_combinations} total combinations")
print(f" - Models: {total_models}")
print(f" - Sparse configs: {total_configs}")
print(f" - Benchmark-subsets: {total_benchmarks}")
- print(f" - Estimated time: {total_combinations * TIMEOUT_PER_BENCHMARK / 3600:.1f} hours (worst case)")
-
+ print(
+ f" - Estimated time: {total_combinations * TIMEOUT_PER_BENCHMARK / 3600:.1f} hours (worst case)"
+ )
+
# Create executor
print(f"\n๐ง Initializing BenchmarkExecutor...")
executor = BenchmarkExecutor(
@@ -241,9 +271,9 @@
enable_resumability=ENABLE_RESUMABILITY,
required_result_files=["raw_results.csv"],
timeout_per_benchmark=TIMEOUT_PER_BENCHMARK,
- verbose=True
+ verbose=True,
)
-
+
# Run benchmarks
print(f"\n๐ฏ Running Benchmark Matrix...")
try:
@@ -253,9 +283,9 @@
benchmark_configs=BENCHMARKS,
adapter_config=ADAPTER_CONFIG,
generation_kwargs=GENERATION_KWARGS,
- request_kwargs=REQUEST_KWARGS
+ request_kwargs=REQUEST_KWARGS,
)
-
+
# Print summary
print(f"\nโ
Benchmark Execution Completed!")
print(f" - Total: {results.progress.total_stubs}")
@@ -263,10 +293,10 @@
print(f" - Failed: {results.progress.failed_stubs}")
print(f" - Skipped: {results.progress.skipped_stubs}")
print(f" - Results saved to: {RESULT_DIR}")
-
+
except KeyboardInterrupt:
print(f"\nโ ๏ธ Interrupted by user")
print(f" Partial results in: {RESULT_DIR}")
except Exception as e:
print(f"\nโ Execution failed: {e}")
- raise
+ raise
diff --git a/benchmark/scripts/full_benchmarking/full_benchmark.py b/benchmark/scripts/full_benchmarking/full_benchmark.py
index d37d4b7b..1fe318bf 100644
--- a/benchmark/scripts/full_benchmarking/full_benchmark.py
+++ b/benchmark/scripts/full_benchmarking/full_benchmark.py
@@ -42,7 +42,9 @@
from sparse_attention_hub.adapters.model_servers.huggingface import ModelServerHF
## spin a model server ###
-model_server = ModelServerHF(ModelServerConfig(enable_stats_logging=True, delete_on_zero_reference=False))
+model_server = ModelServerHF(
+ ModelServerConfig(enable_stats_logging=True, delete_on_zero_reference=False)
+)
# ============================================================================
# CONFIGURATION
@@ -503,7 +505,7 @@ def get_random_sampling_config_name(sink_size, window_size, sampling_rate):
),
]
-#SPARSE_CONFIGS = [("dense", None)]
+# SPARSE_CONFIGS = [("dense", None)]
# ==========================================================================
# Benchmark List
@@ -578,7 +580,7 @@ def get_random_sampling_config_name(sink_size, window_size, sampling_rate):
# mock_benchmark_config,
# niah1, niah2, niah3, cwe, fwe, vt, qa1, qa2, multikey1, multikey2, multikey3, multikey, multivalue
cwe,
- niah1
+ niah1,
]
diff --git a/benchmark/scripts/full_benchmarking/generate_results_table.py b/benchmark/scripts/full_benchmarking/generate_results_table.py
index 701ace74..8f4fbc51 100755
--- a/benchmark/scripts/full_benchmarking/generate_results_table.py
+++ b/benchmark/scripts/full_benchmarking/generate_results_table.py
@@ -22,40 +22,40 @@
def find_dataset_files(base_folder: str, dataset_name: str) -> List[Path]:
"""
Find all files corresponding to the given dataset in the base folder.
-
+
Args:
base_folder: Path to the base folder containing benchmark results
dataset_name: Name of the dataset to search for
-
+
Returns:
List of paths to dataset directories
"""
base_path = Path(base_folder)
dataset_paths = []
-
+
# Walk through the directory structure
for model_dir in base_path.iterdir():
if not model_dir.is_dir():
continue
-
+
for config_dir in model_dir.iterdir():
if not config_dir.is_dir():
continue
-
+
dataset_dir = config_dir / dataset_name
if dataset_dir.exists():
dataset_paths.append(dataset_dir)
-
+
return dataset_paths
def extract_config_name(dataset_path: Path) -> str:
"""
Extract the configuration name from the dataset path.
-
+
Args:
dataset_path: Path to the dataset directory
-
+
Returns:
Configuration name
"""
@@ -66,49 +66,51 @@ def extract_config_name(dataset_path: Path) -> str:
def parse_metrics_json(metrics_file: Path) -> Dict[str, Any]:
"""
Parse the metrics.json file to extract macro metrics.
-
+
Args:
metrics_file: Path to the metrics.json file
-
+
Returns:
Dictionary containing the parsed metrics
"""
try:
- with open(metrics_file, 'r') as f:
+ with open(metrics_file, "r") as f:
data = json.load(f)
-
+
metrics = {}
-
+
# Extract overall score
- if 'overall_score' in data:
- metrics['overall_score'] = data['overall_score']
-
+ if "overall_score" in data:
+ metrics["overall_score"] = data["overall_score"]
+
# Extract task-specific scores
- if 'task_scores' in data:
- for task_name, task_metrics in data['task_scores'].items():
+ if "task_scores" in data:
+ for task_name, task_metrics in data["task_scores"].items():
for metric_name, metric_value in task_metrics.items():
# Create a unique metric name if there are multiple tasks
- if len(data['task_scores']) > 1:
+ if len(data["task_scores"]) > 1:
metric_key = f"{task_name}_{metric_name}"
else:
metric_key = metric_name
metrics[metric_key] = metric_value
-
+
return metrics
-
+
except Exception as e:
print(f"Warning: Could not parse {metrics_file}: {e}")
return {}
-def parse_micro_metrics_jsonl(micro_metrics_file: Path, max_lines: int = 1000) -> Dict[str, float]:
+def parse_micro_metrics_jsonl(
+ micro_metrics_file: Path, max_lines: int = 1000
+) -> Dict[str, float]:
"""
Parse the micro_metrics.jsonl file to extract average density and attention error.
-
+
Args:
micro_metrics_file: Path to the micro_metrics.jsonl file
max_lines: Maximum number of lines to read
-
+
Returns:
Dictionary containing average density and attention error
"""
@@ -116,37 +118,37 @@ def parse_micro_metrics_jsonl(micro_metrics_file: Path, max_lines: int = 1000) -
density_values = []
error_values = []
line_count = 0
-
- with open(micro_metrics_file, 'r') as f:
+
+ with open(micro_metrics_file, "r") as f:
for line in f:
if line_count >= max_lines:
break
-
+
try:
data = json.loads(line.strip())
- metric_name = data.get('metric', '')
- value = data.get('value', 0.0)
-
- if metric_name == 'research_attention_density':
+ metric_name = data.get("metric", "")
+ value = data.get("value", 0.0)
+
+ if metric_name == "research_attention_density":
density_values.append(value)
- elif metric_name == 'research_attention_output_error':
+ elif metric_name == "research_attention_output_error":
error_values.append(value)
-
+
except json.JSONDecodeError:
continue
-
+
line_count += 1
-
+
micro_metrics = {}
-
+
if density_values:
- micro_metrics['avg_density'] = sum(density_values) / len(density_values)
-
+ micro_metrics["avg_density"] = sum(density_values) / len(density_values)
+
if error_values:
- micro_metrics['avg_attention_error'] = sum(error_values) / len(error_values)
-
+ micro_metrics["avg_attention_error"] = sum(error_values) / len(error_values)
+
return micro_metrics
-
+
except Exception as e:
print(f"Warning: Could not parse {micro_metrics_file}: {e}")
return {}
@@ -155,97 +157,97 @@ def parse_micro_metrics_jsonl(micro_metrics_file: Path, max_lines: int = 1000) -
def get_all_available_metrics(dataset_paths: List[Path]) -> List[str]:
"""
Get all available metrics across all dataset paths.
-
+
Args:
dataset_paths: List of paths to dataset directories
-
+
Returns:
List of all available metric names
"""
all_metrics = set()
-
+
for dataset_path in dataset_paths:
metrics_file = dataset_path / "metrics.json"
if metrics_file.exists():
metrics = parse_metrics_json(metrics_file)
all_metrics.update(metrics.keys())
-
+
micro_metrics_file = dataset_path / "micro_metrics.jsonl"
if micro_metrics_file.exists():
micro_metrics = parse_micro_metrics_jsonl(micro_metrics_file)
all_metrics.update(micro_metrics.keys())
-
+
return sorted(list(all_metrics))
def generate_results_table(dataset_name: str, base_folder: str) -> pd.DataFrame:
"""
Generate a results table from benchmark data.
-
+
Args:
dataset_name: Name of the dataset to analyze
base_folder: Path to the base folder containing benchmark results
-
+
Returns:
DataFrame with configs as rows and metrics as columns
"""
print(f"๐ Finding dataset files for '{dataset_name}' in '{base_folder}'...")
dataset_paths = find_dataset_files(base_folder, dataset_name)
-
+
if not dataset_paths:
print(f"โ No dataset files found for '{dataset_name}' in '{base_folder}'")
return pd.DataFrame()
-
+
print(f"โ
Found {len(dataset_paths)} dataset directories")
-
+
# Get all available metrics
print("๐ Discovering available metrics...")
all_metrics = get_all_available_metrics(dataset_paths)
print(f"โ
Found {len(all_metrics)} metrics: {', '.join(all_metrics)}")
-
+
# Initialize results dictionary
results = []
-
+
print("๐ Processing each configuration...")
for dataset_path in dataset_paths:
config_name = extract_config_name(dataset_path)
print(f" Processing: {config_name}")
-
- row_data = {'config': config_name}
-
+
+ row_data = {"config": config_name}
+
# Parse macro metrics
metrics_file = dataset_path / "metrics.json"
if metrics_file.exists():
macro_metrics = parse_metrics_json(metrics_file)
row_data.update(macro_metrics)
-
+
# Parse micro metrics
micro_metrics_file = dataset_path / "micro_metrics.jsonl"
if micro_metrics_file.exists():
micro_metrics = parse_micro_metrics_jsonl(micro_metrics_file)
row_data.update(micro_metrics)
-
+
results.append(row_data)
-
+
# Create DataFrame
df = pd.DataFrame(results)
-
+
if df.empty:
print("โ No results found")
return df
-
+
# Reorder columns to put config first, then metrics
- if 'config' in df.columns:
- other_cols = [col for col in df.columns if col != 'config']
- df = df[['config'] + other_cols]
-
+ if "config" in df.columns:
+ other_cols = [col for col in df.columns if col != "config"]
+ df = df[["config"] + other_cols]
+
return df
def save_results_table(df: pd.DataFrame, dataset_name: str, base_folder: str) -> None:
"""
Save the results table to files.
-
+
Args:
df: DataFrame containing the results
dataset_name: Name of the dataset
@@ -254,21 +256,21 @@ def save_results_table(df: pd.DataFrame, dataset_name: str, base_folder: str) ->
if df.empty:
print("โ No data to save")
return
-
+
# Create output directory
output_dir = Path(base_folder) / "results_tables"
output_dir.mkdir(exist_ok=True)
-
+
# Save as CSV
csv_file = output_dir / f"{dataset_name}_results.csv"
df.to_csv(csv_file, index=False)
print(f"๐พ Saved CSV results to: {csv_file}")
-
+
# Save as Excel
excel_file = output_dir / f"{dataset_name}_results.xlsx"
df.to_excel(excel_file, index=False)
print(f"๐พ Saved Excel results to: {excel_file}")
-
+
# Print summary
print(f"\n๐ Results Summary:")
print(f" - Configurations: {len(df)}")
@@ -285,43 +287,42 @@ def main():
Examples:
python generate_results_table.py loogle_shortdep_cloze full_benchmark.matrix
python generate_results_table.py longbench_passage_retrieval_en ./results/
- """
+ """,
)
-
+
parser.add_argument(
"dataset_name",
- help="Name of the dataset to analyze (e.g., loogle_shortdep_cloze)"
+ help="Name of the dataset to analyze (e.g., loogle_shortdep_cloze)",
)
-
+
parser.add_argument(
- "base_folder",
- help="Path to the base folder containing benchmark results"
+ "base_folder", help="Path to the base folder containing benchmark results"
)
-
+
args = parser.parse_args()
-
+
print("๐ Starting Results Table Generation")
print("=" * 50)
print(f"Dataset: {args.dataset_name}")
print(f"Base folder: {args.base_folder}")
print()
-
+
# Generate the results table
df = generate_results_table(args.dataset_name, args.base_folder)
-
+
if not df.empty:
# Display the table
print("\n๐ Results Table:")
print("=" * 80)
print(df.to_string(index=False))
-
+
# Save the results
save_results_table(df, args.dataset_name, args.base_folder)
-
+
print(f"\nโ
Results table generation completed successfully!")
else:
print(f"\nโ No results found for dataset '{args.dataset_name}'")
if __name__ == "__main__":
- main()
\ No newline at end of file
+ main()
diff --git a/benchmark/scripts/micro_benchmarking/analyse_stress_tests.py b/benchmark/scripts/micro_benchmarking/analyse_stress_tests.py
index ba3e3444..8529d271 100644
--- a/benchmark/scripts/micro_benchmarking/analyse_stress_tests.py
+++ b/benchmark/scripts/micro_benchmarking/analyse_stress_tests.py
@@ -20,7 +20,7 @@
def parse_config_name(config_name: str) -> Dict[str, float]:
"""Parse configuration name to extract parameters.
-
+
Args:
config_name: Configuration name like "adaptive_sampling.sink_0.001_window_0.001_heavy_0.005_base_0.01_epsilon_0.01_delta_0.01"
or "oracle_top_k_0.5.sink_0.02_window_0.02"
@@ -28,16 +28,16 @@ def parse_config_name(config_name: str) -> Dict[str, float]:
or "hashattention.sink_0.001_window_0.001_top_k_0.17"
or "adaptive_sampling_hat.sink_0.01_window_0.01_heavy_0.02_base_0.01_epsilon_0.5_delta_0.5"
or "random_sampling.sink_0.001_window_0.001_sampling_rate_0.01"
-
+
Returns:
Dictionary with parsed parameters
"""
# Extract parameters using regex for different configuration types
-
+
# Pattern for adaptive_sampling_hat (must come before adaptive_sampling to avoid conflicts)
adaptive_hat_pattern = r"adaptive_sampling_hat\.sink_([\d.]+)_window_([\d.]+)_heavy_([\d.]+)_base_([\d.]+)_epsilon_([\d.]+)_delta_([\d.]+)"
adaptive_hat_match = re.match(adaptive_hat_pattern, config_name)
-
+
if adaptive_hat_match:
return {
"config_type": "adaptive_sampling_hat",
@@ -46,13 +46,13 @@ def parse_config_name(config_name: str) -> Dict[str, float]:
"heavy_size": float(adaptive_hat_match.group(3)),
"base_rate_sampling": float(adaptive_hat_match.group(4)),
"epsilon": float(adaptive_hat_match.group(5)),
- "delta": float(adaptive_hat_match.group(6))
+ "delta": float(adaptive_hat_match.group(6)),
}
-
+
# Pattern for adaptive_sampling
adaptive_pattern = r"adaptive_sampling\.sink_([\d.]+)_window_([\d.]+)_heavy_([\d.]+)_base_([\d.]+)_epsilon_([\d.]+)_delta_([\d.]+)"
adaptive_match = re.match(adaptive_pattern, config_name)
-
+
if adaptive_match:
return {
"config_type": "adaptive_sampling",
@@ -61,97 +61,103 @@ def parse_config_name(config_name: str) -> Dict[str, float]:
"heavy_size": float(adaptive_match.group(3)),
"base_rate_sampling": float(adaptive_match.group(4)),
"epsilon": float(adaptive_match.group(5)),
- "delta": float(adaptive_match.group(6))
+ "delta": float(adaptive_match.group(6)),
}
-
+
# Pattern for oracle_top_k
top_k_pattern = r"oracle_top_k_([\d.]+)\.sink_([\d.]+)_window_([\d.]+)"
top_k_match = re.match(top_k_pattern, config_name)
-
+
if top_k_match:
return {
"config_type": "oracle_top_k",
"top_k": float(top_k_match.group(1)),
"sink_size": float(top_k_match.group(2)),
- "window_size": float(top_k_match.group(3))
+ "window_size": float(top_k_match.group(3)),
}
-
+
# Pattern for oracle_top_p
top_p_pattern = r"oracle_top_p_([\d.]+)\.sink_([\d.]+)_window_([\d.]+)"
top_p_match = re.match(top_p_pattern, config_name)
-
+
if top_p_match:
return {
"config_type": "oracle_top_p",
"top_p": float(top_p_match.group(1)),
"sink_size": float(top_p_match.group(2)),
- "window_size": float(top_p_match.group(3))
+ "window_size": float(top_p_match.group(3)),
}
-
+
# Pattern for hashattention
- hashattention_pattern = r"hashattention\.sink_([\d.]+)_window_([\d.]+)_top_k_([\d.]+)"
+ hashattention_pattern = (
+ r"hashattention\.sink_([\d.]+)_window_([\d.]+)_top_k_([\d.]+)"
+ )
hashattention_match = re.match(hashattention_pattern, config_name)
-
+
if hashattention_match:
return {
"config_type": "hashattention",
"sink_size": float(hashattention_match.group(1)),
"window_size": float(hashattention_match.group(2)),
- "hat_top_k": float(hashattention_match.group(3))
+ "hat_top_k": float(hashattention_match.group(3)),
}
-
+
# Pattern for random_sampling
- random_sampling_pattern = r"random_sampling\.sink_([\d.]+)_window_([\d.]+)_sampling_rate_([\d.]+)"
+ random_sampling_pattern = (
+ r"random_sampling\.sink_([\d.]+)_window_([\d.]+)_sampling_rate_([\d.]+)"
+ )
random_sampling_match = re.match(random_sampling_pattern, config_name)
-
+
if random_sampling_match:
return {
"config_type": "random_sampling",
"sink_size": float(random_sampling_match.group(1)),
"window_size": float(random_sampling_match.group(2)),
- "sampling_rate": float(random_sampling_match.group(3))
+ "sampling_rate": float(random_sampling_match.group(3)),
}
-
+
# If no pattern matches, return empty dict
return {"config_type": "unknown"}
def load_config_file(config_path: Path) -> Dict[str, Any]:
"""Load configuration from JSON file.
-
+
Args:
config_path: Path to config.json file
-
+
Returns:
Configuration dictionary
"""
- with open(config_path, 'r') as f:
+ with open(config_path, "r") as f:
return json.load(f)
def load_micro_metrics(metrics_path: Path) -> List[Dict[str, Any]]:
"""Load micro metrics from JSONL file.
-
+
Args:
metrics_path: Path to micro_metrics.jsonl file
-
+
Returns:
List of metric entries
"""
metrics = []
- with open(metrics_path, 'r') as f:
+ with open(metrics_path, "r") as f:
for line in f:
if line.strip():
metrics.append(json.loads(line))
return metrics
-def process_experiment_directory(exp_dir: Path) -> List[Tuple[List[Dict[str, Any]], Dict[str, Any], str]]:
+def process_experiment_directory(
+ exp_dir: Path,
+) -> List[Tuple[List[Dict[str, Any]], Dict[str, Any], str]]:
"""Process a single experiment directory.
-
+
Args:
exp_dir: Path to experiment directory
-
+
Returns:
List of tuples (metrics_data, config_data, dataset_name) for each benchmark directory
"""
@@ -159,45 +165,45 @@ def process_experiment_directory(exp_dir: Path) -> List[Tuple[List[Dict[str, Any
benchmark_dirs = [d for d in exp_dir.iterdir() if d.is_dir()]
if not benchmark_dirs:
return []
-
+
results = []
-
+
for benchmark_dir in benchmark_dirs:
dataset_name = benchmark_dir.name
-
+
# Load configuration
config_path = benchmark_dir / "config.json"
if not config_path.exists():
continue
-
+
config = load_config_file(config_path)
-
+
# Load micro metrics
metrics_path = benchmark_dir / "micro_metrics.jsonl"
if not metrics_path.exists():
continue
-
+
metrics = load_micro_metrics(metrics_path)
-
+
results.append((metrics, config, dataset_name))
-
+
return results
def extract_sparse_config_params(config: Dict[str, Any]) -> Dict[str, Any]:
"""Extract sparse attention configuration parameters.
-
+
Args:
config: Configuration dictionary
-
+
Returns:
Dictionary with sparse attention parameters
"""
sparse_config = config.get("sparse_attention_config", {})
masker_configs = sparse_config.get("masker_configs", [])
-
+
params = {}
-
+
# Extract parameters from masker configs
for masker_config in masker_configs:
if "sink_size" in masker_config:
@@ -226,55 +232,61 @@ def extract_sparse_config_params(config: Dict[str, Any]) -> Dict[str, Any]:
elif "sampling_rate" in masker_config:
# Random sampling parameters
params["sampling_rate"] = masker_config["sampling_rate"]
-
+
return params
-def organize_metrics_by_layer(metrics: List[Dict[str, Any]]) -> Dict[int, Dict[str, float]]:
+def organize_metrics_by_layer(
+ metrics: List[Dict[str, Any]]
+) -> Dict[int, Dict[str, float]]:
"""Organize metrics by layer index and average multiple measurements.
-
+
Args:
metrics: List of metric entries
-
+
Returns:
Dictionary mapping layer_idx to averaged metrics
"""
layer_metrics = {}
-
+
# First pass: collect all values for each layer
for metric in metrics:
layer_idx = metric.get("metadata", {}).get("layer_idx")
if layer_idx is None:
continue
-
+
if layer_idx not in layer_metrics:
layer_metrics[layer_idx] = {"density": [], "error": []}
-
+
metric_name = metric.get("metric")
value = metric.get("value")
-
+
if metric_name == "research_attention_density":
layer_metrics[layer_idx]["density"].append(value)
elif metric_name == "research_attention_output_error":
layer_metrics[layer_idx]["error"].append(value)
-
+
# Second pass: average the collected values
averaged_metrics = {}
for layer_idx, values in layer_metrics.items():
averaged_metrics[layer_idx] = {}
-
+
if values["density"]:
- averaged_metrics[layer_idx]["density"] = sum(values["density"]) / len(values["density"])
-
+ averaged_metrics[layer_idx]["density"] = sum(values["density"]) / len(
+ values["density"]
+ )
+
if values["error"]:
- averaged_metrics[layer_idx]["error"] = sum(values["error"]) / len(values["error"])
-
+ averaged_metrics[layer_idx]["error"] = sum(values["error"]) / len(
+ values["error"]
+ )
+
return averaged_metrics
def analyze_stress_tests(results_dir: str, output_dir: str) -> None:
"""Analyze stress test results and generate TSV files.
-
+
Args:
results_dir: Path to stress_test_adaptive.matrix directory
output_dir: Output directory for TSV files
@@ -282,39 +294,39 @@ def analyze_stress_tests(results_dir: str, output_dir: str) -> None:
results_path = Path(results_dir)
output_path = Path(output_dir)
output_path.mkdir(exist_ok=True)
-
+
# Find model directories
model_dirs = [d for d in results_path.iterdir() if d.is_dir()]
-
+
all_vector_data = []
all_metadata = []
-
+
for model_dir in model_dirs:
model_name = model_dir.name
-
+
# Find configuration directories
config_dirs = [d for d in model_dir.iterdir() if d.is_dir()]
-
+
for config_dir in config_dirs:
config_name = config_dir.name
-
+
# Parse configuration name
parsed_params = parse_config_name(config_name)
-
+
# Process experiment directory (now returns list of results for each benchmark)
benchmark_results = process_experiment_directory(config_dir)
-
+
if not benchmark_results:
continue
-
+
# Process each benchmark result
for metrics, config, dataset_name in benchmark_results:
# Extract sparse attention parameters
sparse_params = extract_sparse_config_params(config)
-
+
# Organize metrics by layer
layer_metrics = organize_metrics_by_layer(metrics)
-
+
# Generate vector data
for layer_idx, layer_data in layer_metrics.items():
if "density" in layer_data and "error" in layer_data:
@@ -323,10 +335,10 @@ def analyze_stress_tests(results_dir: str, output_dir: str) -> None:
"config": config_name,
"layer_idx": layer_idx,
"density": layer_data["density"],
- "error": layer_data["error"]
+ "error": layer_data["error"],
}
all_vector_data.append(vector_entry)
-
+
# Generate metadata entry
metadata_entry = {
"model": model_name,
@@ -334,46 +346,44 @@ def analyze_stress_tests(results_dir: str, output_dir: str) -> None:
"dataset": dataset_name,
"layer_idx": "all", # This will be expanded for each layer
**parsed_params,
- **sparse_params
+ **sparse_params,
}
-
+
# Add metadata for each layer
for layer_idx in layer_metrics.keys():
layer_metadata = metadata_entry.copy()
layer_metadata["layer_idx"] = layer_idx
all_metadata.append(layer_metadata)
-
+
# Write vector.tsv
vector_path = output_path / "vector.tsv"
metadata_path = output_path / "metadata.tsv"
-
+
# Write vector data
- with open(vector_path, 'w') as f:
+ with open(vector_path, "w") as f:
f.write("density\terror\n")
for entry in all_vector_data:
f.write(f"{entry['density']}\t{entry['error']}\n")
-
+
# Write metadata
- with open(metadata_path, 'w') as f:
+ with open(metadata_path, "w") as f:
if all_metadata:
# Get all unique keys from all metadata entries
all_keys = set()
for entry in all_metadata:
all_keys.update(entry.keys())
-
+
# Sort keys for consistent output
sorted_keys = sorted(all_keys)
-
+
# Write header
f.write("\t".join(sorted_keys) + "\n")
-
+
# Write data
for entry in all_metadata:
row = [str(entry.get(key, "")) for key in sorted_keys]
f.write("\t".join(row) + "\n")
-
-
print(f"Analysis complete!")
print(f"Vector data written to: {vector_path}")
print(f"Metadata written to: {metadata_path}")
@@ -385,25 +395,25 @@ def main():
"""Main function."""
parser = argparse.ArgumentParser(description="Analyze stress test results")
parser.add_argument(
- "--results-dir",
+ "--results-dir",
default="./stress_test_adaptive.matrix",
- help="Path to stress test results directory"
+ help="Path to stress test results directory",
)
parser.add_argument(
- "--output-dir",
+ "--output-dir",
default="./sparse-attention-hub-share/docs/micro_tests/",
- help="Output directory for TSV files"
+ help="Output directory for TSV files",
)
-
+
args = parser.parse_args()
-
+
# Check if results directory exists
if not os.path.exists(args.results_dir):
print(f"Error: Results directory '{args.results_dir}' does not exist")
return
-
+
analyze_stress_tests(args.results_dir, args.output_dir)
if __name__ == "__main__":
- main()
+ main()
diff --git a/benchmark/scripts/micro_benchmarking/plot_stress_test_results.py b/benchmark/scripts/micro_benchmarking/plot_stress_test_results.py
index 001bcc8e..1fa50339 100644
--- a/benchmark/scripts/micro_benchmarking/plot_stress_test_results.py
+++ b/benchmark/scripts/micro_benchmarking/plot_stress_test_results.py
@@ -20,29 +20,31 @@
def load_tsv_data(vector_path: str, metadata_path: str) -> pd.DataFrame:
"""Load and merge vector and metadata TSV files.
-
+
Args:
vector_path: Path to vector.tsv file
metadata_path: Path to metadata.tsv file
-
+
Returns:
Merged DataFrame with all data
"""
# Load the data
- vector_df = pd.read_csv(vector_path, sep='\t')
- metadata_df = pd.read_csv(metadata_path, sep='\t')
-
+ vector_df = pd.read_csv(vector_path, sep="\t")
+ metadata_df = pd.read_csv(metadata_path, sep="\t")
+
# Merge the dataframes
# Since both files have the same number of rows in the same order,
# we can simply concatenate them
merged_df = pd.concat([vector_df, metadata_df], axis=1)
-
+
return merged_df
-def create_interactive_scatter_plot(df: pd.DataFrame, output_path: str, dataset_name: str = "") -> None:
+def create_interactive_scatter_plot(
+ df: pd.DataFrame, output_path: str, dataset_name: str = ""
+) -> None:
"""Create an interactive scatter plot of error vs density.
-
+
Args:
df: DataFrame containing the data
output_path: Path to save the HTML plot
@@ -50,21 +52,28 @@ def create_interactive_scatter_plot(df: pd.DataFrame, output_path: str, dataset_
"""
# Create the scatter plot
fig = go.Figure()
-
+
# Define colors for different configuration types
config_colors = {
- 'adaptive_sampling': '#1f77b4', # Blue
- 'oracle_top_k': '#ff7f0e', # Orange
- 'oracle_top_p': '#2ca02c', # Green
- 'hashattention': '#d62728', # Red
- 'adaptive_sampling_hat': '#9467bd', # Purple
- 'random_sampling': '#8c564b' # Brown
+ "adaptive_sampling": "#1f77b4", # Blue
+ "oracle_top_k": "#ff7f0e", # Orange
+ "oracle_top_p": "#2ca02c", # Green
+ "hashattention": "#d62728", # Red
+ "adaptive_sampling_hat": "#9467bd", # Purple
+ "random_sampling": "#8c564b", # Brown
}
-
+
# Add scatter traces for each configuration type
- for config_type in ['adaptive_sampling', 'oracle_top_k', 'oracle_top_p', 'hashattention', 'adaptive_sampling_hat', 'random_sampling']:
- config_data = df[df['config_type'] == config_type]
-
+ for config_type in [
+ "adaptive_sampling",
+ "oracle_top_k",
+ "oracle_top_p",
+ "hashattention",
+ "adaptive_sampling_hat",
+ "random_sampling",
+ ]:
+ config_data = df[df["config_type"] == config_type]
+
if len(config_data) > 0:
# Create hover text for this configuration type
hover_text = []
@@ -77,20 +86,20 @@ def create_interactive_scatter_plot(df: pd.DataFrame, output_path: str, dataset_
Sink Size: {row.get('sink_size', 'N/A')}
Window Size: {row.get('window_size', 'N/A')}
"""
-
+
# Add configuration-specific parameters
- if config_type == 'adaptive_sampling':
+ if config_type == "adaptive_sampling":
config_info += f"""
Heavy Size: {row.get('heavy_size', 'N/A')}
Base Rate: {row.get('base_rate_sampling', 'N/A')}
Epsilon: {row.get('epsilon', 'N/A')}
Delta: {row.get('delta', 'N/A')}
"""
- elif config_type == 'oracle_top_k':
+ elif config_type == "oracle_top_k":
config_info += f"Top-K: {row.get('top_k', 'N/A')} "
- elif config_type == 'oracle_top_p':
+ elif config_type == "oracle_top_p":
config_info += f"Top-P: {row.get('top_p', 'N/A')} "
- elif config_type == 'hashattention':
+ elif config_type == "hashattention":
config_info += f"""
Hash Top-K: {row.get('hat_top_k', 'N/A')}
Hash Heavy Size: {row.get('hat_heavy_size', 'N/A')}
@@ -99,7 +108,7 @@ def create_interactive_scatter_plot(df: pd.DataFrame, output_path: str, dataset_
Hash MLP Hidden Size: {row.get('hat_mlp_hidden_size', 'N/A')}
Hash MLP Activation: {row.get('hat_mlp_activation', 'N/A')}
"""
- elif config_type == 'adaptive_sampling_hat':
+ elif config_type == "adaptive_sampling_hat":
config_info += f"""
Heavy Size: {row.get('heavy_size', 'N/A')}
Base Rate: {row.get('base_rate_sampling', 'N/A')}
@@ -111,74 +120,69 @@ def create_interactive_scatter_plot(df: pd.DataFrame, output_path: str, dataset_
Hash MLP Hidden Size: {row.get('hat_mlp_hidden_size', 'N/A')}
Hash MLP Activation: {row.get('hat_mlp_activation', 'N/A')}
"""
- elif config_type == 'random_sampling':
- config_info += f"Sampling Rate: {row.get('sampling_rate', 'N/A')} "
-
+ elif config_type == "random_sampling":
+ config_info += (
+ f"Sampling Rate: {row.get('sampling_rate', 'N/A')} "
+ )
+
config_info += f"""
Density: {row.get('density', 'N/A'):.4f}
Error: {row.get('error', 'N/A'):.4f}
"""
hover_text.append(config_info)
-
+
fig.add_trace(
go.Scatter(
- x=config_data['density'],
- y=config_data['error'],
- mode='markers',
- marker=dict(
- size=6,
- color=config_colors[config_type],
- opacity=0.7
- ),
+ x=config_data["density"],
+ y=config_data["error"],
+ mode="markers",
+ marker=dict(size=6, color=config_colors[config_type], opacity=0.7),
text=hover_text,
- hoverinfo='text',
- hovertemplate='%{text} ',
- name=config_type.replace('_', ' ').title()
+ hoverinfo="text",
+ hovertemplate="%{text} ",
+ name=config_type.replace("_", " ").title(),
)
)
-
+
# Create title with dataset name if provided
- title_text = 'Sparse Attention: Error vs Density (All Configurations)'
+ title_text = "Sparse Attention: Error vs Density (All Configurations)"
if dataset_name:
- title_text = f'Sparse Attention: Error vs Density - {dataset_name}'
-
+ title_text = f"Sparse Attention: Error vs Density - {dataset_name}"
+
# Update layout
fig.update_layout(
- title={
- 'text': title_text,
- 'x': 0.5,
- 'xanchor': 'center',
- 'font': {'size': 20}
- },
- xaxis_title='Density',
- yaxis_title='Error',
+ title={"text": title_text, "x": 0.5, "xanchor": "center", "font": {"size": 20}},
+ xaxis_title="Density",
+ yaxis_title="Error",
xaxis=dict(
title_font=dict(size=16),
tickfont=dict(size=12),
- gridcolor='lightgray',
- zeroline=False
+ gridcolor="lightgray",
+ zeroline=False,
),
yaxis=dict(
title_font=dict(size=16),
tickfont=dict(size=12),
- gridcolor='lightgray',
- zeroline=False
+ gridcolor="lightgray",
+ zeroline=False,
),
- plot_bgcolor='white',
- hovermode='closest',
+ plot_bgcolor="white",
+ hovermode="closest",
width=1000,
height=700,
- showlegend=True
+ showlegend=True,
)
-
+
# Save the plot
fig.write_html(output_path)
print(f"Interactive plot saved to: {output_path}")
-def create_configuration_analysis_plots(df: pd.DataFrame, output_dir: str, dataset_name: str = "") -> None:
+def create_configuration_analysis_plots(
+ df: pd.DataFrame, output_dir: str, dataset_name: str = ""
+) -> None:
"""Create additional analysis plots for different configurations.
-
+
Args:
df: DataFrame containing the data
output_dir: Directory to save the plots
@@ -186,212 +190,264 @@ def create_configuration_analysis_plots(df: pd.DataFrame, output_dir: str, datas
"""
output_path = Path(output_dir)
output_path.mkdir(exist_ok=True)
-
+
# 1. Plot by configuration type with subplots
- config_types = df['config_type'].unique()
-
+ config_types = df["config_type"].unique()
+
fig = make_subplots(
- rows=1, cols=len(config_types),
+ rows=1,
+ cols=len(config_types),
subplot_titles=[f"{ct.replace('_', ' ').title()}" for ct in config_types],
- specs=[[{"secondary_y": False}] * len(config_types)]
+ specs=[[{"secondary_y": False}] * len(config_types)],
)
-
- colors = ['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728', '#9467bd', '#8c564b'] # Blue, Orange, Green, Red, Purple, Brown
-
+
+ colors = [
+ "#1f77b4",
+ "#ff7f0e",
+ "#2ca02c",
+ "#d62728",
+ "#9467bd",
+ "#8c564b",
+ ] # Blue, Orange, Green, Red, Purple, Brown
+
for i, config_type in enumerate(config_types):
- config_data = df[df['config_type'] == config_type]
-
+ config_data = df[df["config_type"] == config_type]
+
fig.add_trace(
go.Scatter(
- x=config_data['density'],
- y=config_data['error'],
- mode='markers',
+ x=config_data["density"],
+ y=config_data["error"],
+ mode="markers",
marker=dict(size=8, color=colors[i], opacity=0.7),
- name=config_type.replace('_', ' ').title(),
- text=[f"Layer: {layer} Density: {d:.4f} Error: {e:.4f}"
- for layer, d, e in zip(config_data['layer_idx'], config_data['density'], config_data['error'])],
- hoverinfo='text',
- showlegend=False
+ name=config_type.replace("_", " ").title(),
+ text=[
+ f"Layer: {layer} Density: {d:.4f} Error: {e:.4f}"
+ for layer, d, e in zip(
+ config_data["layer_idx"],
+ config_data["density"],
+ config_data["error"],
+ )
+ ],
+ hoverinfo="text",
+ showlegend=False,
),
- row=1, col=i+1
+ row=1,
+ col=i + 1,
)
-
+
# Create title with dataset name if provided
- title_text = 'Error vs Density by Configuration Type'
+ title_text = "Error vs Density by Configuration Type"
if dataset_name:
- title_text = f'Error vs Density by Configuration Type - {dataset_name}'
-
- fig.update_layout(
- title=title_text,
- showlegend=False,
- width=1200,
- height=500
- )
-
+ title_text = f"Error vs Density by Configuration Type - {dataset_name}"
+
+ fig.update_layout(title=title_text, showlegend=False, width=1200, height=500)
+
fig.write_html(output_path / "config_type_analysis.html")
- print(f"Configuration type analysis plot saved to: {output_path / 'config_type_analysis.html'}")
-
+ print(
+ f"Configuration type analysis plot saved to: {output_path / 'config_type_analysis.html'}"
+ )
+
# 2. Layer-wise analysis with configuration type colors
- layer_groups = df.groupby('layer_idx')
-
+ layer_groups = df.groupby("layer_idx")
+
fig2 = go.Figure()
-
+
# Define colors for different configuration types
config_colors = {
- 'adaptive_sampling': '#1f77b4', # Blue
- 'oracle_top_k': '#ff7f0e', # Orange
- 'oracle_top_p': '#2ca02c', # Green
- 'hashattention': '#d62728', # Red
- 'adaptive_sampling_hat': '#9467bd', # Purple
- 'random_sampling': '#8c564b' # Brown
+ "adaptive_sampling": "#1f77b4", # Blue
+ "oracle_top_k": "#ff7f0e", # Orange
+ "oracle_top_p": "#2ca02c", # Green
+ "hashattention": "#d62728", # Red
+ "adaptive_sampling_hat": "#9467bd", # Purple
+ "random_sampling": "#8c564b", # Brown
}
-
+
for layer_idx, group in layer_groups:
# Color by configuration type
- colors_for_layer = [config_colors.get(ct, '#000000') for ct in group['config_type']]
-
+ colors_for_layer = [
+ config_colors.get(ct, "#000000") for ct in group["config_type"]
+ ]
+
fig2.add_trace(
go.Scatter(
- x=group['density'],
- y=group['error'],
- mode='markers',
+ x=group["density"],
+ y=group["error"],
+ mode="markers",
marker=dict(size=6, color=colors_for_layer, opacity=0.7),
- name=f'Layer {layer_idx}',
- text=[f"Config: {config} Type: {ct} Density: {d:.4f} Error: {e:.4f}"
- for config, ct, d, e in zip(group['config'], group['config_type'], group['density'], group['error'])],
- hoverinfo='text'
+ name=f"Layer {layer_idx}",
+ text=[
+ f"Config: {config} Type: {ct} Density: {d:.4f} Error: {e:.4f}"
+ for config, ct, d, e in zip(
+ group["config"],
+ group["config_type"],
+ group["density"],
+ group["error"],
+ )
+ ],
+ hoverinfo="text",
)
)
-
+
# Create title with dataset name if provided
- title_text2 = 'Error vs Density by Layer (Colored by Config Type)'
+ title_text2 = "Error vs Density by Layer (Colored by Config Type)"
if dataset_name:
- title_text2 = f'Error vs Density by Layer (Colored by Config Type) - {dataset_name}'
-
+ title_text2 = (
+ f"Error vs Density by Layer (Colored by Config Type) - {dataset_name}"
+ )
+
fig2.update_layout(
title=title_text2,
- xaxis_title='Density',
- yaxis_title='Error',
+ xaxis_title="Density",
+ yaxis_title="Error",
width=1000,
- height=700
+ height=700,
)
-
+
fig2.write_html(output_path / "layer_analysis.html")
print(f"Layer analysis plot saved to: {output_path / 'layer_analysis.html'}")
-
+
# 3. Configuration comparison plot
fig3 = go.Figure()
-
- for config_type in ['adaptive_sampling', 'oracle_top_k', 'oracle_top_p', 'hashattention', 'adaptive_sampling_hat', 'random_sampling']:
- config_data = df[df['config_type'] == config_type]
-
+
+ for config_type in [
+ "adaptive_sampling",
+ "oracle_top_k",
+ "oracle_top_p",
+ "hashattention",
+ "adaptive_sampling_hat",
+ "random_sampling",
+ ]:
+ config_data = df[df["config_type"] == config_type]
+
if len(config_data) > 0:
# Calculate average error and density for each configuration
- config_avg = config_data.groupby('config').agg({
- 'error': 'mean',
- 'density': 'mean'
- }).reset_index()
-
+ config_avg = (
+ config_data.groupby("config")
+ .agg({"error": "mean", "density": "mean"})
+ .reset_index()
+ )
+
fig3.add_trace(
go.Scatter(
- x=config_avg['density'],
- y=config_avg['error'],
- mode='markers',
- marker=dict(
- size=10,
- color=config_colors[config_type],
- opacity=0.8
- ),
- name=config_type.replace('_', ' ').title(),
- text=[f"Config: {config} Avg Density: {d:.4f} Avg Error: {e:.4f}"
- for config, d, e in zip(config_avg['config'], config_avg['density'], config_avg['error'])],
- hoverinfo='text'
+ x=config_avg["density"],
+ y=config_avg["error"],
+ mode="markers",
+ marker=dict(size=10, color=config_colors[config_type], opacity=0.8),
+ name=config_type.replace("_", " ").title(),
+ text=[
+ f"Config: {config} Avg Density: {d:.4f} Avg Error: {e:.4f}"
+ for config, d, e in zip(
+ config_avg["config"],
+ config_avg["density"],
+ config_avg["error"],
+ )
+ ],
+ hoverinfo="text",
)
)
-
+
# Create title with dataset name if provided
- title_text3 = 'Average Error vs Density by Configuration'
+ title_text3 = "Average Error vs Density by Configuration"
if dataset_name:
- title_text3 = f'Average Error vs Density by Configuration - {dataset_name}'
-
+ title_text3 = f"Average Error vs Density by Configuration - {dataset_name}"
+
fig3.update_layout(
title=title_text3,
- xaxis_title='Average Density',
- yaxis_title='Average Error',
+ xaxis_title="Average Density",
+ yaxis_title="Average Error",
width=1000,
height=700,
- showlegend=True
+ showlegend=True,
)
-
+
fig3.write_html(output_path / "config_comparison.html")
- print(f"Configuration comparison plot saved to: {output_path / 'config_comparison.html'}")
+ print(
+ f"Configuration comparison plot saved to: {output_path / 'config_comparison.html'}"
+ )
def main():
"""Main function to create interactive plots per dataset."""
- parser = argparse.ArgumentParser(description='Create interactive plots for stress test results')
- parser.add_argument('--vector-file', default='./sparse-attention-hub-share/docs/micro_tests/vector.tsv',
- help='Path to vector.tsv file')
- parser.add_argument('--metadata-file', default='./sparse-attention-hub-share/docs/micro_tests/metadata.tsv',
- help='Path to metadata.tsv file')
- parser.add_argument('--output-dir', default='./sparse-attention-hub-share/docs/micro_tests/plots/',
- help='Output directory for plots')
-
+ parser = argparse.ArgumentParser(
+ description="Create interactive plots for stress test results"
+ )
+ parser.add_argument(
+ "--vector-file",
+ default="./sparse-attention-hub-share/docs/micro_tests/vector.tsv",
+ help="Path to vector.tsv file",
+ )
+ parser.add_argument(
+ "--metadata-file",
+ default="./sparse-attention-hub-share/docs/micro_tests/metadata.tsv",
+ help="Path to metadata.tsv file",
+ )
+ parser.add_argument(
+ "--output-dir",
+ default="./sparse-attention-hub-share/docs/micro_tests/plots/",
+ help="Output directory for plots",
+ )
+
args = parser.parse_args()
-
+
# Load data
print("Loading data...")
df = load_tsv_data(args.vector_file, args.metadata_file)
print(f"Loaded {len(df)} data points")
-
+
# Check if dataset column exists
- if 'dataset' not in df.columns:
- print("Warning: No 'dataset' column found in metadata. Creating plots for all data combined.")
+ if "dataset" not in df.columns:
+ print(
+ "Warning: No 'dataset' column found in metadata. Creating plots for all data combined."
+ )
# Create output directory
output_path = Path(args.output_dir)
output_path.mkdir(exist_ok=True)
-
+
# Create main interactive scatter plot
print("Creating main interactive scatter plot...")
create_interactive_scatter_plot(df, output_path / "error_vs_density.html")
-
+
# Create additional analysis plots
print("Creating additional analysis plots...")
create_configuration_analysis_plots(df, args.output_dir)
-
+
print("All plots created successfully!")
print(f"Main plot: {output_path / 'error_vs_density.html'}")
print(f"Additional plots: {output_path / 'config_analysis.html'}")
print(f"Layer analysis: {output_path / 'layer_analysis.html'}")
return
# Group data by dataset
- datasets = df['dataset'].unique()
+ datasets = df["dataset"].unique()
# Filter out NaN values
datasets = [d for d in datasets if pd.notna(d)]
print(f"Found {len(datasets)} datasets: {datasets}")
-
+
# Create plots for each dataset
for dataset_name in datasets:
print(f"\nProcessing dataset: {dataset_name}")
-
+
# Filter data for this dataset
- dataset_df = df[df['dataset'] == dataset_name]
+ dataset_df = df[df["dataset"] == dataset_name]
print(f" Found {len(dataset_df)} data points for this dataset")
-
+
# Create dataset-specific output directory
dataset_output_dir = Path(args.output_dir) / dataset_name
dataset_output_dir.mkdir(parents=True, exist_ok=True)
-
+
# Create main interactive scatter plot for this dataset
print(f" Creating main interactive scatter plot...")
- create_interactive_scatter_plot(dataset_df, dataset_output_dir / "error_vs_density.html", dataset_name)
-
+ create_interactive_scatter_plot(
+ dataset_df, dataset_output_dir / "error_vs_density.html", dataset_name
+ )
+
# Create additional analysis plots for this dataset
print(f" Creating additional analysis plots...")
- create_configuration_analysis_plots(dataset_df, str(dataset_output_dir), dataset_name)
-
+ create_configuration_analysis_plots(
+ dataset_df, str(dataset_output_dir), dataset_name
+ )
+
print(f" Plots for {dataset_name} saved to: {dataset_output_dir}")
-
+
print(f"\nAll plots created successfully!")
print(f"Plots organized by dataset in: {args.output_dir}/")
for dataset_name in datasets:
@@ -399,4 +455,4 @@ def main():
if __name__ == "__main__":
- main()
+ main()
diff --git a/benchmark/scripts/micro_benchmarking/stress_tests_adaptive_matrix.py b/benchmark/scripts/micro_benchmarking/stress_tests_adaptive_matrix.py
index 88d919ed..235540e4 100644
--- a/benchmark/scripts/micro_benchmarking/stress_tests_adaptive_matrix.py
+++ b/benchmark/scripts/micro_benchmarking/stress_tests_adaptive_matrix.py
@@ -20,12 +20,20 @@
from benchmark.executor import BenchmarkExecutor
from benchmark.executor_config import BenchmarkConfig, AdapterConfig
-from sparse_attention_hub.sparse_attention.research_attention import ResearchAttentionConfig
+from sparse_attention_hub.sparse_attention.research_attention import (
+ ResearchAttentionConfig,
+)
from sparse_attention_hub.sparse_attention.research_attention.maskers.fixed.implementations import (
- LocalMaskerConfig, SinkMaskerConfig, OracleTopKConfig, OracleTopPMaskerConfig, HashAttentionTopKMaskerConfig
+ LocalMaskerConfig,
+ SinkMaskerConfig,
+ OracleTopKConfig,
+ OracleTopPMaskerConfig,
+ HashAttentionTopKMaskerConfig,
)
from sparse_attention_hub.sparse_attention.research_attention.maskers.sampling.implementations import (
- AdaptiveSamplingMaskerConfig, RandomSamplingMaskerConfig, MagicPigConfig
+ AdaptiveSamplingMaskerConfig,
+ RandomSamplingMaskerConfig,
+ MagicPigConfig,
)
# ============================================================================
@@ -40,185 +48,380 @@
# Model List
MODELS = [
- "meta-llama/Llama-3.1-8B-Instruct",
+ "meta-llama/Llama-3.1-8B-Instruct",
]
-def get_adaptive_config_name(sink_size, window_size, heavy_size, base_rate_sampling, epsilon, delta):
+
+def get_adaptive_config_name(
+ sink_size, window_size, heavy_size, base_rate_sampling, epsilon, delta
+):
return f"adaptive_sampling.sink_{sink_size}_window_{window_size}_heavy_{heavy_size}_base_{base_rate_sampling}_epsilon_{epsilon}_delta_{delta}"
-def get_adaptive_hat_config_name(sink_size, window_size, heavy_size, base_rate_sampling, epsilon, delta):
+
+def get_adaptive_hat_config_name(
+ sink_size, window_size, heavy_size, base_rate_sampling, epsilon, delta
+):
return f"adaptive_sampling_hat.sink_{sink_size}_window_{window_size}_heavy_{heavy_size}_base_{base_rate_sampling}_epsilon_{epsilon}_delta_{delta}"
+
def get_oracle_top_p_config_name(sink_size, window_size, top_p):
return f"oracle_top_p_{top_p}.sink_{sink_size}_window_{window_size}"
+
def get_oracle_top_k_config_name(sink_size, window_size, top_k):
return f"oracle_top_k_{top_k}.sink_{sink_size}_window_{window_size}"
+
def get_hashattention_config_name(sink_size, window_size, top_k):
return f"hashattention.sink_{sink_size}_window_{window_size}_top_k_{top_k}"
+
def get_random_sampling_config_name(sink_size, window_size, sampling_rate):
return f"random_sampling.sink_{sink_size}_window_{window_size}_sampling_rate_{sampling_rate}"
+
# Sparse Attention Configurations
SPARSE_CONFIGS = []
# adaptive sampling + oracle top k + sink + window
-for (epsilon, delta) in [(0.01, 0.01), (0.05, 0.05), (0.1, 0.1), (0.25, 0.25), (0.3, 0.3), (0.4, 0.4), (0.5, 0.5)]:
- for (sink_size, window_size) in [(0.001, 0.001), (0.005, 0.005), (0.01, 0.01), (0.02, 0.02)]:
+for epsilon, delta in [
+ (0.01, 0.01),
+ (0.05, 0.05),
+ (0.1, 0.1),
+ (0.25, 0.25),
+ (0.3, 0.3),
+ (0.4, 0.4),
+ (0.5, 0.5),
+]:
+ for sink_size, window_size in [
+ (0.001, 0.001),
+ (0.005, 0.005),
+ (0.01, 0.01),
+ (0.02, 0.02),
+ ]:
for heavy_size in [0.005, 0.01, 0.02, 0.05]:
for base_rate_sampling in [0.01, 0.05, 0.1]:
- SPARSE_CONFIGS.append((get_adaptive_config_name(sink_size, window_size, heavy_size, base_rate_sampling, epsilon, delta), ResearchAttentionConfig(masker_configs=[
- SinkMaskerConfig(sink_size=sink_size),
- LocalMaskerConfig(window_size=window_size),
- OracleTopKConfig(heavy_size=heavy_size),
- AdaptiveSamplingMaskerConfig(base_rate_sampling=base_rate_sampling, epsilon=epsilon, delta=delta, init_offset=sink_size, local_offset=window_size)
- ])))
+ SPARSE_CONFIGS.append(
+ (
+ get_adaptive_config_name(
+ sink_size,
+ window_size,
+ heavy_size,
+ base_rate_sampling,
+ epsilon,
+ delta,
+ ),
+ ResearchAttentionConfig(
+ masker_configs=[
+ SinkMaskerConfig(sink_size=sink_size),
+ LocalMaskerConfig(window_size=window_size),
+ OracleTopKConfig(heavy_size=heavy_size),
+ AdaptiveSamplingMaskerConfig(
+ base_rate_sampling=base_rate_sampling,
+ epsilon=epsilon,
+ delta=delta,
+ init_offset=sink_size,
+ local_offset=window_size,
+ ),
+ ]
+ ),
+ )
+ )
# oracle top p + sink + window
-for (sink_size, window_size) in [(0.001, 0.001), (0.005, 0.005), (0.01, 0.01), (0.02, 0.02)]:
+for sink_size, window_size in [
+ (0.001, 0.001),
+ (0.005, 0.005),
+ (0.01, 0.01),
+ (0.02, 0.02),
+]:
for top_p in [0.5, 0.75, 0.9, 0.95, 0.99]:
- SPARSE_CONFIGS.append((get_oracle_top_p_config_name(sink_size, window_size, top_p), ResearchAttentionConfig(masker_configs=[
- SinkMaskerConfig(sink_size=sink_size),
- LocalMaskerConfig(window_size=window_size),
- OracleTopPMaskerConfig(top_p=top_p)
- ])))
+ SPARSE_CONFIGS.append(
+ (
+ get_oracle_top_p_config_name(sink_size, window_size, top_p),
+ ResearchAttentionConfig(
+ masker_configs=[
+ SinkMaskerConfig(sink_size=sink_size),
+ LocalMaskerConfig(window_size=window_size),
+ OracleTopPMaskerConfig(top_p=top_p),
+ ]
+ ),
+ )
+ )
# oracle top p + sink + window
-for (sink_size, window_size) in [(0.001, 0.001), (0.005, 0.005), (0.01, 0.01), (0.02, 0.02)]:
- for top_k in [0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09, 0.1, 0.11, 0.12, 0.13, 0.14, 0.15, 0.16, 0.17, 0.18, 0.19, 0.2, 0.21, 0.22, 0.23, 0.24, 0.25, 0.26, 0.27, 0.28, 0.29, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9]:
- SPARSE_CONFIGS.append((get_oracle_top_k_config_name(sink_size, window_size, top_k), ResearchAttentionConfig(masker_configs=[
- SinkMaskerConfig(sink_size=sink_size),
- LocalMaskerConfig(window_size=window_size),
- OracleTopKConfig(heavy_size=top_k)
- ])))
+for sink_size, window_size in [
+ (0.001, 0.001),
+ (0.005, 0.005),
+ (0.01, 0.01),
+ (0.02, 0.02),
+]:
+ for top_k in [
+ 0.01,
+ 0.02,
+ 0.03,
+ 0.04,
+ 0.05,
+ 0.06,
+ 0.07,
+ 0.08,
+ 0.09,
+ 0.1,
+ 0.11,
+ 0.12,
+ 0.13,
+ 0.14,
+ 0.15,
+ 0.16,
+ 0.17,
+ 0.18,
+ 0.19,
+ 0.2,
+ 0.21,
+ 0.22,
+ 0.23,
+ 0.24,
+ 0.25,
+ 0.26,
+ 0.27,
+ 0.28,
+ 0.29,
+ 0.3,
+ 0.4,
+ 0.5,
+ 0.6,
+ 0.7,
+ 0.8,
+ 0.9,
+ ]:
+ SPARSE_CONFIGS.append(
+ (
+ get_oracle_top_k_config_name(sink_size, window_size, top_k),
+ ResearchAttentionConfig(
+ masker_configs=[
+ SinkMaskerConfig(sink_size=sink_size),
+ LocalMaskerConfig(window_size=window_size),
+ OracleTopKConfig(heavy_size=top_k),
+ ]
+ ),
+ )
+ )
# usa_weight_file = "/home/apd10/HashAttention-1.0/artifacts/llama3.1-8b-patch.64K.v1.pt"
-weight_file = "/home/apd10/HashAttention-1.0/artifacts/llama3.1-8b-patch.64K.v1.hat_weights.pkl"
+weight_file = (
+ "/home/apd10/HashAttention-1.0/artifacts/llama3.1-8b-patch.64K.v1.hat_weights.pkl"
+)
# from sparse_attention_hub.sparse_attention.utils.hashattention_utils import create_hat_weights_file_from_usa
-#create_hat_weights_file_from_usa(usa_weight_file, weight_file, num_layers=32, num_heads=32, device="cpu")
+# create_hat_weights_file_from_usa(usa_weight_file, weight_file, num_layers=32, num_heads=32, device="cpu")
# weight_dictionary = convert_usa_weights_to_hash_attention(weight_file, num_layers=32, num_heads=32, device="cpu")
# hashattention + sink + window
-for (sink_size, window_size) in [(0.001, 0.001), (0.005, 0.005), (0.01, 0.01), (0.02, 0.02)]:
- for top_k in [0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09, 0.1, 0.11, 0.12, 0.13, 0.14, 0.15, 0.16, 0.17, 0.18, 0.19, 0.2, 0.21, 0.22, 0.23, 0.24, 0.25, 0.26, 0.27, 0.28, 0.25, 0.3, 0.4, 0.5]:
- SPARSE_CONFIGS.append((get_hashattention_config_name(sink_size, window_size, top_k), ResearchAttentionConfig(masker_configs=[
- SinkMaskerConfig(sink_size=sink_size),
- LocalMaskerConfig(window_size=window_size),
- HashAttentionTopKMaskerConfig(heavy_size=top_k,
- hat_bits=32,
- hat_mlp_layers=3,
- hat_mlp_hidden_size=128,
- hat_mlp_activation="silu",
- hat_weight_file=weight_file,
- hat_weights=None),
- ])))
+for sink_size, window_size in [
+ (0.001, 0.001),
+ (0.005, 0.005),
+ (0.01, 0.01),
+ (0.02, 0.02),
+]:
+ for top_k in [
+ 0.01,
+ 0.02,
+ 0.03,
+ 0.04,
+ 0.05,
+ 0.06,
+ 0.07,
+ 0.08,
+ 0.09,
+ 0.1,
+ 0.11,
+ 0.12,
+ 0.13,
+ 0.14,
+ 0.15,
+ 0.16,
+ 0.17,
+ 0.18,
+ 0.19,
+ 0.2,
+ 0.21,
+ 0.22,
+ 0.23,
+ 0.24,
+ 0.25,
+ 0.26,
+ 0.27,
+ 0.28,
+ 0.25,
+ 0.3,
+ 0.4,
+ 0.5,
+ ]:
+ SPARSE_CONFIGS.append(
+ (
+ get_hashattention_config_name(sink_size, window_size, top_k),
+ ResearchAttentionConfig(
+ masker_configs=[
+ SinkMaskerConfig(sink_size=sink_size),
+ LocalMaskerConfig(window_size=window_size),
+ HashAttentionTopKMaskerConfig(
+ heavy_size=top_k,
+ hat_bits=32,
+ hat_mlp_layers=3,
+ hat_mlp_hidden_size=128,
+ hat_mlp_activation="silu",
+ hat_weight_file=weight_file,
+ hat_weights=None,
+ ),
+ ]
+ ),
+ )
+ )
# adaptive sampling + hat top k + sink + window
-for (epsilon, delta) in [(0.01, 0.01), (0.05, 0.05), (0.1, 0.1), (0.25, 0.25), (0.3, 0.3), (0.4, 0.4), (0.5, 0.5)]:
- for (sink_size, window_size) in [(0.001, 0.001), (0.005, 0.005), (0.01, 0.01), (0.02, 0.02)]:
+for epsilon, delta in [
+ (0.01, 0.01),
+ (0.05, 0.05),
+ (0.1, 0.1),
+ (0.25, 0.25),
+ (0.3, 0.3),
+ (0.4, 0.4),
+ (0.5, 0.5),
+]:
+ for sink_size, window_size in [
+ (0.001, 0.001),
+ (0.005, 0.005),
+ (0.01, 0.01),
+ (0.02, 0.02),
+ ]:
for heavy_size in [0.005, 0.01, 0.02, 0.05, 0.1]:
for base_rate_sampling in [0.01, 0.05, 0.1]:
- SPARSE_CONFIGS.append((get_adaptive_hat_config_name(sink_size, window_size, heavy_size, base_rate_sampling, epsilon, delta), ResearchAttentionConfig(masker_configs=[
- SinkMaskerConfig(sink_size=sink_size),
- LocalMaskerConfig(window_size=window_size),
- HashAttentionTopKMaskerConfig(heavy_size=heavy_size,
- hat_bits=32,
- hat_mlp_layers=3,
- hat_mlp_hidden_size=128,
- hat_mlp_activation="silu",
- hat_weight_file=weight_file,
- hat_weights=None),
- AdaptiveSamplingMaskerConfig(base_rate_sampling=base_rate_sampling, epsilon=epsilon, delta=delta, init_offset=sink_size, local_offset=window_size)
- ])))
+ SPARSE_CONFIGS.append(
+ (
+ get_adaptive_hat_config_name(
+ sink_size,
+ window_size,
+ heavy_size,
+ base_rate_sampling,
+ epsilon,
+ delta,
+ ),
+ ResearchAttentionConfig(
+ masker_configs=[
+ SinkMaskerConfig(sink_size=sink_size),
+ LocalMaskerConfig(window_size=window_size),
+ HashAttentionTopKMaskerConfig(
+ heavy_size=heavy_size,
+ hat_bits=32,
+ hat_mlp_layers=3,
+ hat_mlp_hidden_size=128,
+ hat_mlp_activation="silu",
+ hat_weight_file=weight_file,
+ hat_weights=None,
+ ),
+ AdaptiveSamplingMaskerConfig(
+ base_rate_sampling=base_rate_sampling,
+ epsilon=epsilon,
+ delta=delta,
+ init_offset=sink_size,
+ local_offset=window_size,
+ ),
+ ]
+ ),
+ )
+ )
# random sampling + sink + window
-for (epsilon, delta) in [(0.01, 0.01), (0.05, 0.05), (0.1, 0.1), (0.25, 0.25), (0.3, 0.3), (0.4, 0.4), (0.5, 0.5)]:
- for (sink_size, window_size) in [(0.001, 0.001), (0.005, 0.005), (0.01, 0.01), (0.02, 0.02)]:
+for epsilon, delta in [
+ (0.01, 0.01),
+ (0.05, 0.05),
+ (0.1, 0.1),
+ (0.25, 0.25),
+ (0.3, 0.3),
+ (0.4, 0.4),
+ (0.5, 0.5),
+]:
+ for sink_size, window_size in [
+ (0.001, 0.001),
+ (0.005, 0.005),
+ (0.01, 0.01),
+ (0.02, 0.02),
+ ]:
for sampling_rate in [0.01, 0.05, 0.1, 0.2, 0.3, 0.4, 0.5]:
- SPARSE_CONFIGS.append((get_random_sampling_config_name(sink_size, window_size, sampling_rate), ResearchAttentionConfig(masker_configs=[
- SinkMaskerConfig(sink_size=sink_size),
- LocalMaskerConfig(window_size=window_size),
- RandomSamplingMaskerConfig(sampling_rate=sampling_rate)
- ])))
-
-
-#
+ SPARSE_CONFIGS.append(
+ (
+ get_random_sampling_config_name(
+ sink_size, window_size, sampling_rate
+ ),
+ ResearchAttentionConfig(
+ masker_configs=[
+ SinkMaskerConfig(sink_size=sink_size),
+ LocalMaskerConfig(window_size=window_size),
+ RandomSamplingMaskerConfig(sampling_rate=sampling_rate),
+ ]
+ ),
+ )
+ )
+
+
+#
print("total number of configs: ", len(SPARSE_CONFIGS))
# Benchmark List
# 1. InfiniteBench - using passkey task
infinite_bench_config = BenchmarkConfig(
- benchmark_name="infinite_bench",
- subsets=["passkey"]
+ benchmark_name="infinite_bench", subsets=["passkey"]
)
# 2. Ruler - using 4096 context length
-ruler_config = BenchmarkConfig(
- benchmark_name="ruler",
- subsets=["4096"]
-)
+ruler_config = BenchmarkConfig(benchmark_name="ruler", subsets=["4096"])
# 3. Loogle - using shortdep_qa task
loogle_config = BenchmarkConfig(
benchmark_name="loogle",
- #subsets=["shortdep_qa"],
+ # subsets=["shortdep_qa"],
subsets=["longdep_qa"],
- #subsets=["shortdep_cloze"],
- #subsets=["longdep_summarization"],
+ # subsets=["shortdep_cloze"],
+ # subsets=["longdep_summarization"],
)
# 4. ZeroScrolls - using gov_report task
zero_scrolls_config = BenchmarkConfig(
- benchmark_name="zero_scrolls",
- subsets=["default"]
+ benchmark_name="zero_scrolls", subsets=["default"]
)
# 5. LongBenchv2 - using 0shot task
-longbenchv2_config = BenchmarkConfig(
- benchmark_name="longbenchv2",
- subsets=["0shot"]
-)
+longbenchv2_config = BenchmarkConfig(benchmark_name="longbenchv2", subsets=["0shot"])
# 6. AIME2024 - using single task
-aime2024_config = BenchmarkConfig(
- benchmark_name="aime2024",
- subsets=["aime2024"]
-)
+aime2024_config = BenchmarkConfig(benchmark_name="aime2024", subsets=["aime2024"])
# 7. AIME2025 - using single task
-aime2025_config = BenchmarkConfig(
- benchmark_name="aime2025",
- subsets=["aime2025"]
-)
+aime2025_config = BenchmarkConfig(benchmark_name="aime2025", subsets=["aime2025"])
# 8. LongBench (existing) - using narrativeqa task
longbench_config = BenchmarkConfig(
- benchmark_name="longbench",
- subsets=["passage_retrieval_en"]
+ benchmark_name="longbench", subsets=["passage_retrieval_en"]
)
# 9. Mock Benchmark (existing) - using single task
mock_benchmark_config = BenchmarkConfig(
- benchmark_name="mock_benchmark",
- subsets=["reading_comprehension"]
+ benchmark_name="mock_benchmark", subsets=["reading_comprehension"]
)
# List of all sample configurations
BENCHMARKS = [
- #infinite_bench_config,
- #ruler_config,
+ # infinite_bench_config,
+ # ruler_config,
loogle_config,
- #zero_scrolls_config,
- #longbenchv2_config,
- #aime2024_config,
- #aime2025_config,
- #longbench_config,
- #mock_benchmark_config
+ # zero_scrolls_config,
+ # longbenchv2_config,
+ # aime2024_config,
+ # aime2025_config,
+ # longbench_config,
+ # mock_benchmark_config
]
@@ -231,7 +434,7 @@ def get_random_sampling_config_name(sink_size, window_size, sampling_rate):
},
tokenizer_kwargs={
"padding_side": "left",
- }
+ },
)
# Generation Parameters
@@ -244,10 +447,7 @@ def get_random_sampling_config_name(sink_size, window_size, sampling_rate):
}
# Request Parameters
-REQUEST_KWARGS = {
- "max_context_length": 32000,
- "max_requests": 1
-}
+REQUEST_KWARGS = {"max_context_length": 32000, "max_requests": 1}
# Execution Settings
RESULT_DIR = "./stress_test_adaptive.matrix/"
@@ -261,7 +461,7 @@ def get_random_sampling_config_name(sink_size, window_size, sampling_rate):
if __name__ == "__main__":
print("๐ Starting Minimalistic Benchmark Suite")
print("=" * 50)
-
+
print(f"๐ง Configuration:")
print(f" - GPUs: {GPUS}")
print(f" - Models: {len(MODELS)}")
@@ -278,25 +478,29 @@ def get_random_sampling_config_name(sink_size, window_size, sampling_rate):
print(f" - Benchmarks: {len(BENCHMARKS)}")
for i, benchmark in enumerate(BENCHMARKS, 1):
if benchmark.subsets:
- print(f" {i}. {benchmark.benchmark_name}: {len(benchmark.subsets)} subsets")
+ print(
+ f" {i}. {benchmark.benchmark_name}: {len(benchmark.subsets)} subsets"
+ )
else:
print(f" {i}. {benchmark.benchmark_name}: all subsets")
print(f" - Max concurrent: {MAX_CONCURRENT_RUNS}")
print(f" - Result dir: {RESULT_DIR}")
print(f" - Resumability: {'enabled' if ENABLE_RESUMABILITY else 'disabled'}")
-
+
# Calculate total combinations
total_models = len(MODELS)
total_configs = len(SPARSE_CONFIGS)
total_benchmarks = sum(len(b.subsets) if b.subsets else 1 for b in BENCHMARKS)
total_combinations = total_models * total_configs * total_benchmarks
-
+
print(f"\n๐ Experiment Matrix: {total_combinations} total combinations")
print(f" - Models: {total_models}")
print(f" - Sparse configs: {total_configs}")
print(f" - Benchmark-subsets: {total_benchmarks}")
- print(f" - Estimated time: {total_combinations * TIMEOUT_PER_BENCHMARK / 3600:.1f} hours (worst case)")
-
+ print(
+ f" - Estimated time: {total_combinations * TIMEOUT_PER_BENCHMARK / 3600:.1f} hours (worst case)"
+ )
+
# Create executor
print(f"\n๐ง Initializing BenchmarkExecutor...")
executor = BenchmarkExecutor(
@@ -306,9 +510,9 @@ def get_random_sampling_config_name(sink_size, window_size, sampling_rate):
enable_resumability=ENABLE_RESUMABILITY,
required_result_files=["raw_results.csv"],
timeout_per_benchmark=TIMEOUT_PER_BENCHMARK,
- verbose=True
+ verbose=True,
)
-
+
# Run benchmarks
print(f"\n๐ฏ Running Benchmark Matrix...")
try:
@@ -318,9 +522,9 @@ def get_random_sampling_config_name(sink_size, window_size, sampling_rate):
benchmark_configs=BENCHMARKS,
adapter_config=ADAPTER_CONFIG,
generation_kwargs=GENERATION_KWARGS,
- request_kwargs=REQUEST_KWARGS
+ request_kwargs=REQUEST_KWARGS,
)
-
+
# Print summary
print(f"\nโ
Benchmark Execution Completed!")
print(f" - Total: {results.progress.total_stubs}")
@@ -328,10 +532,10 @@ def get_random_sampling_config_name(sink_size, window_size, sampling_rate):
print(f" - Failed: {results.progress.failed_stubs}")
print(f" - Skipped: {results.progress.skipped_stubs}")
print(f" - Results saved to: {RESULT_DIR}")
-
+
except KeyboardInterrupt:
print(f"\nโ ๏ธ Interrupted by user")
print(f" Partial results in: {RESULT_DIR}")
except Exception as e:
print(f"\nโ Execution failed: {e}")
- raise
+ raise
diff --git a/benchmark/scripts/single_benchmark_model_example.py b/benchmark/scripts/single_benchmark_model_example.py
index b0418fc1..f5c014eb 100644
--- a/benchmark/scripts/single_benchmark_model_example.py
+++ b/benchmark/scripts/single_benchmark_model_example.py
@@ -24,46 +24,64 @@
import sys
# Change to directory two levels below current location
-os.chdir('/workspace/sparse-attention-hub')
-sys.path.insert(0, '/workspace/sparse-attention-hub')
+os.chdir("/workspace/sparse-attention-hub")
+sys.path.insert(0, "/workspace/sparse-attention-hub")
-from sparse_attention_hub.sparse_attention.research_attention import ResearchAttentionConfig
+from sparse_attention_hub.sparse_attention.research_attention import (
+ ResearchAttentionConfig,
+)
from sparse_attention_hub.sparse_attention.research_attention.maskers.fixed.implementations import (
- LocalMaskerConfig, SinkMaskerConfig, OracleTopKConfig
+ LocalMaskerConfig,
+ SinkMaskerConfig,
+ OracleTopKConfig,
)
from sparse_attention_hub.sparse_attention.research_attention.maskers.sampling.implementations import (
- AdaptiveSamplingMaskerConfig
+ AdaptiveSamplingMaskerConfig,
)
from benchmark.ruler32k import Ruler32K
from sparse_attention_hub.adapters import ModelAdapterHF
+
def main():
model_name = "meta-llama/Llama-3.1-8B-Instruct"
- device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
-
- sparse_attention_config = ResearchAttentionConfig(masker_configs=[
- SinkMaskerConfig(sink_size=128),
- LocalMaskerConfig(window_size=128),
- OracleTopKConfig(heavy_size=128),
- AdaptiveSamplingMaskerConfig(base_rate_sampling=0.05, epsilon=0.25, delta=0.25, init_offset=128, local_offset=128)
- ])
-
+ device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
+
+ sparse_attention_config = ResearchAttentionConfig(
+ masker_configs=[
+ SinkMaskerConfig(sink_size=128),
+ LocalMaskerConfig(window_size=128),
+ OracleTopKConfig(heavy_size=128),
+ AdaptiveSamplingMaskerConfig(
+ base_rate_sampling=0.05,
+ epsilon=0.25,
+ delta=0.25,
+ init_offset=128,
+ local_offset=128,
+ ),
+ ]
+ )
+
print(" โ Loading model...")
adapter = ModelAdapterHF(
model_name=model_name,
sparse_attention_config=sparse_attention_config,
- model_kwargs= {"torch_dtype": torch.bfloat16},
+ model_kwargs={"torch_dtype": torch.bfloat16},
generate_kwargs={"max_new_tokens": 32},
- device=device
+ device=device,
)
-
- benchmark = Ruler32K(['vt'])
+
+ benchmark = Ruler32K(["vt"])
result_dir = Path("./test_results")
result_dir.mkdir(exist_ok=True)
- benchmark.run_benchmark(adapter, result_dir, request_kwargs={"max_requests": 1, "max_context_length": 1000000})
-
+ benchmark.run_benchmark(
+ adapter,
+ result_dir,
+ request_kwargs={"max_requests": 1, "max_context_length": 1000000},
+ )
+
+
if __name__ == "__main__":
- main()
+ main()
diff --git a/benchmark/utils/__init__.py b/benchmark/utils/__init__.py
index c6afe576..b5da8152 100644
--- a/benchmark/utils/__init__.py
+++ b/benchmark/utils/__init__.py
@@ -12,7 +12,7 @@
construct_result_directory,
create_result_directory,
validate_result_directory_path,
- ensure_result_directory
+ ensure_result_directory,
)
from .gpu import (
@@ -21,24 +21,19 @@
acquire_gpu_from_pool,
release_gpu_to_pool,
validate_gpu_for_model,
- cleanup_gpu_memory
+ cleanup_gpu_memory,
)
-from .data import (
- escape_dataframe_for_csv,
- save_dataframe_to_csv,
- make_serializable
-)
+from .data import escape_dataframe_for_csv, save_dataframe_to_csv, make_serializable
# Re-export all utilities for backward compatibility
__all__ = [
# Path utilities
"sanitize_model_name",
- "construct_result_directory",
+ "construct_result_directory",
"create_result_directory",
"validate_result_directory_path",
"ensure_result_directory",
-
# GPU utilities
"validate_gpu_availability",
"create_gpu_pool",
@@ -46,9 +41,8 @@
"release_gpu_to_pool",
"validate_gpu_for_model",
"cleanup_gpu_memory",
-
# Data utilities
"escape_dataframe_for_csv",
"save_dataframe_to_csv",
- "make_serializable"
-]
\ No newline at end of file
+ "make_serializable",
+]
diff --git a/benchmark/utils/data.py b/benchmark/utils/data.py
index 20791831..7d161ac9 100644
--- a/benchmark/utils/data.py
+++ b/benchmark/utils/data.py
@@ -13,13 +13,13 @@
def make_serializable(obj: Any) -> Any:
"""Convert non-serializable objects to strings recursively, including dataclasses.
-
+
Args:
obj: Object to make JSON serializable
-
+
Returns:
JSON serializable version of the object
-
+
Example:
>>> config = {"torch_dtype": torch.bfloat16, "device": "cuda"}
>>> serializable = make_serializable(config)
@@ -29,13 +29,13 @@ def make_serializable(obj: Any) -> Any:
return {k: make_serializable(v) for k, v in dataclasses.asdict(obj).items()}
if isinstance(obj, (torch.dtype, torch.device)):
return str(obj)
- elif hasattr(obj, 'dtype'):
+ elif hasattr(obj, "dtype"):
return str(obj)
elif isinstance(obj, dict):
return {k: make_serializable(v) for k, v in obj.items()}
elif isinstance(obj, (list, tuple)):
return [make_serializable(item) for item in obj]
- elif hasattr(obj, '__dict__'):
+ elif hasattr(obj, "__dict__"):
# For custom objects
return make_serializable(vars(obj))
elif obj is None:
@@ -50,16 +50,16 @@ def make_serializable(obj: Any) -> Any:
def escape_dataframe_for_csv(df: pd.DataFrame) -> pd.DataFrame:
"""Escape special characters in DataFrame for safe CSV export.
-
+
This function prepares a DataFrame for CSV export by escaping newlines,
carriage returns, and other special characters that could cause parsing issues.
-
+
Args:
df: DataFrame to escape
-
+
Returns:
DataFrame with escaped string columns
-
+
Example:
>>> df = pd.DataFrame({
... 'text': ['Line 1\nLine 2', 'Text with, comma']
@@ -68,34 +68,36 @@ def escape_dataframe_for_csv(df: pd.DataFrame) -> pd.DataFrame:
>>> escaped_df.to_csv('output.csv', quoting=csv.QUOTE_ALL)
"""
escaped_df = df.copy()
-
+
for col in escaped_df.columns:
- if escaped_df[col].dtype == 'object': # Only process string columns
- escaped_df[col] = escaped_df[col].astype(str).str.replace('\n', '\\n').str.replace('\r', '\\r')
-
+ if escaped_df[col].dtype == "object": # Only process string columns
+ escaped_df[col] = (
+ escaped_df[col]
+ .astype(str)
+ .str.replace("\n", "\\n")
+ .str.replace("\r", "\\r")
+ )
+
return escaped_df
def save_dataframe_to_csv(
- df: pd.DataFrame,
- file_path: str,
- index: bool = False,
- quoting: int = csv.QUOTE_ALL
+ df: pd.DataFrame, file_path: str, index: bool = False, quoting: int = csv.QUOTE_ALL
) -> None:
"""Save DataFrame to CSV with proper escaping and quoting.
-
+
This is a convenience function that combines escaping and CSV export
with consistent settings for benchmark results.
-
+
Args:
df: DataFrame to save
file_path: Path to save the CSV file
index: Whether to include DataFrame index in output
quoting: CSV quoting mode (default: QUOTE_ALL)
-
+
Example:
>>> df = pd.DataFrame({'text': ['Line 1\nLine 2']})
>>> save_dataframe_to_csv(df, 'results.csv')
"""
escaped_df = escape_dataframe_for_csv(df)
- escaped_df.to_csv(file_path, index=index, quoting=quoting)
\ No newline at end of file
+ escaped_df.to_csv(file_path, index=index, quoting=quoting)
diff --git a/benchmark/utils/gpu.py b/benchmark/utils/gpu.py
index 84fd2cf6..cefd4053 100644
--- a/benchmark/utils/gpu.py
+++ b/benchmark/utils/gpu.py
@@ -19,7 +19,7 @@
def get_cuda_visible_devices() -> List[int]:
"""Get the CUDA_VISIBLE_DEVICES environment variable.
-
+
Returns:
CUDA_VISIBLE_DEVICES environment variable, or None if not set
"""
@@ -32,39 +32,39 @@ def get_cuda_visible_devices() -> List[int]:
def validate_gpu_availability(gpu_ids: List[int]) -> None:
"""Validate that specified GPUs are available and accessible.
-
+
Checks that CUDA is available, GPUs exist, and they are accessible.
-
+
Args:
gpu_ids: List of GPU device IDs to validate
-
+
Raises:
RuntimeError: If CUDA is not available
ValueError: If any GPU ID is invalid or inaccessible
OSError: If GPU access fails
-
+
Example:
>>> validate_gpu_availability([0, 1]) # Validates GPUs 0 and 1
>>> # No exception means GPUs are available
"""
if not torch.cuda.is_available():
raise RuntimeError("CUDA is not available")
-
+
if not gpu_ids:
raise ValueError("gpu_ids cannot be empty")
-
+
num_gpus: int = torch.cuda.device_count()
if num_gpus == 0:
raise RuntimeError("No CUDA devices found")
-
+
# Check each GPU ID
for gpu_id in gpu_ids:
if not isinstance(gpu_id, int) or gpu_id < 0:
raise ValueError(f"Invalid GPU ID: {gpu_id}")
-
+
# if gpu_id >= num_gpus:
# raise ValueError(f"GPU {gpu_id} does not exist. Available GPUs: 0-{num_gpus-1}")
-
+
# Test GPU access
try:
with torch.cuda.device(gpu_id):
@@ -76,17 +76,17 @@ def validate_gpu_availability(gpu_ids: List[int]) -> None:
raise OSError(f"Cannot access GPU {gpu_id}: {e}") from e
-def create_gpu_pool(gpu_ids: List[int]) -> 'multiprocessing.Queue[int]':
+def create_gpu_pool(gpu_ids: List[int]) -> "multiprocessing.Queue[int]":
"""Create a multiprocessing queue to manage GPU access.
-
+
Creates a thread-safe queue that workers can use to acquire and release GPUs.
-
+
Args:
gpu_ids: List of GPU device IDs to include in the pool
-
+
Returns:
Multiprocessing queue containing available GPU IDs
-
+
Example:
>>> gpu_pool = create_gpu_pool([0, 1, 2])
>>> gpu_id = acquire_gpu_from_pool(gpu_pool)
@@ -94,30 +94,32 @@ def create_gpu_pool(gpu_ids: List[int]) -> 'multiprocessing.Queue[int]':
>>> release_gpu_to_pool(gpu_pool, gpu_id)
"""
validate_gpu_availability(gpu_ids)
-
+
# Create queue and populate with GPU IDs
- gpu_pool: 'multiprocessing.Queue[int]' = multiprocessing.Queue()
+ gpu_pool: "multiprocessing.Queue[int]" = multiprocessing.Queue()
for gpu_id in gpu_ids:
gpu_pool.put(gpu_id)
-
+
return gpu_pool
-def acquire_gpu_from_pool(gpu_pool: 'multiprocessing.Queue[int]', timeout: float = 30.0) -> int:
+def acquire_gpu_from_pool(
+ gpu_pool: "multiprocessing.Queue[int]", timeout: float = 30.0
+) -> int:
"""Acquire a GPU from the pool with timeout.
-
+
Blocks until a GPU becomes available or timeout is reached.
-
+
Args:
gpu_pool: GPU pool queue
timeout: Maximum time to wait for GPU in seconds
-
+
Returns:
GPU device ID that was acquired
-
+
Raises:
queue.Empty: If no GPU becomes available within timeout
-
+
Example:
>>> gpu_id = acquire_gpu_from_pool(gpu_pool, timeout=60.0)
>>> print(f"Acquired GPU {gpu_id}")
@@ -129,15 +131,15 @@ def acquire_gpu_from_pool(gpu_pool: 'multiprocessing.Queue[int]', timeout: float
raise queue.Empty(f"No GPU available within {timeout} seconds")
-def release_gpu_to_pool(gpu_pool: 'multiprocessing.Queue[int]', gpu_id: int) -> None:
+def release_gpu_to_pool(gpu_pool: "multiprocessing.Queue[int]", gpu_id: int) -> None:
"""Release a GPU back to the pool.
-
+
Returns the GPU to the pool so other workers can use it.
-
+
Args:
gpu_pool: GPU pool queue
gpu_id: GPU device ID to release
-
+
Example:
>>> release_gpu_to_pool(gpu_pool, 0)
>>> # GPU 0 is now available for other workers
@@ -145,23 +147,25 @@ def release_gpu_to_pool(gpu_pool: 'multiprocessing.Queue[int]', gpu_id: int) ->
gpu_pool.put(gpu_id)
-def validate_gpu_for_model(gpu_id: int, model_name: str, check_memory: bool = True) -> Dict[str, Any]:
+def validate_gpu_for_model(
+ gpu_id: int, model_name: str, check_memory: bool = True
+) -> Dict[str, Any]:
"""Validate that a GPU is suitable for running a specific model.
-
+
Performs various checks including memory availability and compute capability.
-
+
Args:
gpu_id: GPU device ID to validate
model_name: Name of the model (for logging purposes)
check_memory: Whether to check available GPU memory
-
+
Returns:
Dictionary containing validation results and GPU information
-
+
Raises:
ValueError: If GPU is not suitable for the model
OSError: If GPU access fails
-
+
Example:
>>> info = validate_gpu_for_model(0, "llama-7b")
>>> print(f"GPU memory: {info['memory_gb']} GB")
@@ -170,11 +174,13 @@ def validate_gpu_for_model(gpu_id: int, model_name: str, check_memory: bool = Tr
with torch.cuda.device(gpu_id):
# Get GPU properties
props = torch.cuda.get_device_properties(gpu_id)
-
+
# Basic validation
if props.major < 7: # Require compute capability 7.0+
- raise ValueError(f"GPU {gpu_id} has insufficient compute capability: {props.major}.{props.minor}")
-
+ raise ValueError(
+ f"GPU {gpu_id} has insufficient compute capability: {props.major}.{props.minor}"
+ )
+
# Memory validation
memory_info: Dict[str, Any] = {}
if check_memory:
@@ -182,40 +188,39 @@ def validate_gpu_for_model(gpu_id: int, model_name: str, check_memory: bool = Tr
allocated_memory = torch.cuda.memory_allocated(gpu_id) / (1024**3)
cached_memory = torch.cuda.memory_reserved(gpu_id) / (1024**3)
free_memory = total_memory - allocated_memory
-
+
memory_info = {
"total_gb": total_memory,
"allocated_gb": allocated_memory,
"cached_gb": cached_memory,
- "free_gb": free_memory
+ "free_gb": free_memory,
}
-
+
# Warn if memory is low (less than 2GB free)
if free_memory < 2.0:
logging.warning(
f"GPU {gpu_id} has low memory for {model_name}: "
f"{free_memory:.1f}GB free out of {total_memory:.1f}GB total"
)
-
+
return {
"gpu_id": gpu_id,
"name": props.name,
"compute_capability": f"{props.major}.{props.minor}",
"memory_gb": props.total_memory / (1024**3),
- **memory_info
+ **memory_info,
}
-
+
except Exception as e:
raise OSError(f"Failed to validate GPU {gpu_id} for {model_name}: {e}") from e
-
def cleanup_gpu_memory(gpu_id: Optional[int] = None) -> None:
"""Clean up GPU memory by emptying CUDA cache.
-
+
Args:
gpu_id: Specific GPU to clean up, or None for current device
-
+
Example:
>>> cleanup_gpu_memory(0) # Clean up GPU 0
>>> cleanup_gpu_memory() # Clean up current device
@@ -225,4 +230,4 @@ def cleanup_gpu_memory(gpu_id: Optional[int] = None) -> None:
with torch.cuda.device(gpu_id):
torch.cuda.empty_cache()
else:
- torch.cuda.empty_cache()
\ No newline at end of file
+ torch.cuda.empty_cache()
diff --git a/benchmark/utils/paths.py b/benchmark/utils/paths.py
index 9b541f5e..3e355de8 100644
--- a/benchmark/utils/paths.py
+++ b/benchmark/utils/paths.py
@@ -15,16 +15,16 @@
def sanitize_model_name(model_name: str) -> str:
"""Sanitize model name for use in directory paths.
-
+
Replaces special characters that are not filesystem-safe with underscores.
This ensures model names can be used as directory names across different operating systems.
-
+
Args:
model_name: Original model name (e.g., "meta-llama/Llama-3.1-8B-Instruct")
-
+
Returns:
Sanitized model name safe for filesystem use
-
+
Example:
>>> sanitized = sanitize_model_name("meta-llama/Llama-3.1-8B-Instruct")
>>> print(sanitized)
@@ -32,18 +32,18 @@ def sanitize_model_name(model_name: str) -> str:
"""
# Characters that need to be replaced: / \ : * ? " < > |
special_chars: str = r'\/\:*?"<>|'
-
+
sanitized: str = model_name
for char in special_chars:
- sanitized = sanitized.replace(char, '_')
-
+ sanitized = sanitized.replace(char, "_")
+
# Remove multiple consecutive underscores
- while '__' in sanitized:
- sanitized = sanitized.replace('__', '_')
-
+ while "__" in sanitized:
+ sanitized = sanitized.replace("__", "_")
+
# Remove leading/trailing underscores
- sanitized = sanitized.strip('_')
-
+ sanitized = sanitized.strip("_")
+
return sanitized
@@ -52,103 +52,102 @@ def construct_result_directory(
model_name: str,
sparse_config_name: str,
benchmark_name: str,
- subset: Optional[str] = None
+ subset: Optional[str] = None,
) -> str:
"""Construct result directory path following the standard hierarchy.
-
+
Creates a path following the pattern:
base_dir/sanitized_model/sparse_config/benchmark_subset/
-
+
Note: No timestamp in path to enable resumability across runs.
-
+
Args:
base_result_dir: Base directory for all benchmark results
model_name: Original model name (will be sanitized)
sparse_config_name: Name of sparse attention configuration
benchmark_name: Name of the benchmark
subset: Optional subset name (appended to benchmark_name with underscore)
-
+
Returns:
Complete path to result directory
-
+
Example:
>>> path = construct_result_directory(
- ... "./results", "meta-llama/Llama-3.1-8B",
+ ... "./results", "meta-llama/Llama-3.1-8B",
... "dense", "longbench", "narrativeqa"
... )
>>> print(path)
"./results/meta_llama_Llama_3.1_8B/dense/longbench_narrativeqa"
"""
sanitized_model: str = sanitize_model_name(model_name)
-
+
# Construct benchmark directory name
benchmark_dir: str = benchmark_name
if subset is not None:
benchmark_dir = f"{benchmark_name}_{subset}"
-
+
# Build path components (no timestamp for resumability)
path_components: list[str] = [
base_result_dir,
sanitized_model,
sparse_config_name,
- benchmark_dir
+ benchmark_dir,
]
-
+
result_path: str = os.path.join(*path_components)
return str(Path(result_path).resolve())
-def create_result_directory(
- result_dir: str,
- create_parents: bool = True
-) -> bool:
+def create_result_directory(result_dir: str, create_parents: bool = True) -> bool:
"""Create result directory with proper error handling.
-
+
Creates the specified directory and all necessary parent directories.
This function is safe to call multiple times on the same path.
-
+
Args:
result_dir: Path to the directory to create
create_parents: Whether to create parent directories if they don't exist
-
+
Returns:
True if directory was created or already exists, False if creation failed
-
+
Raises:
ValueError: If result_dir is empty or invalid
PermissionError: If insufficient permissions to create directory
OSError: If directory creation fails for other reasons
-
+
Example:
>>> success = create_result_directory("/path/to/results/model_experiment")
>>> print(success) # True if successful
"""
if not result_dir or not result_dir.strip():
raise ValueError("result_dir cannot be empty")
-
+
result_path = Path(result_dir.strip())
-
+
try:
# Create directory and parents if needed
result_path.mkdir(parents=create_parents, exist_ok=True)
-
+
# Verify directory was created and is writable
if not result_path.exists():
return False
-
+
if not result_path.is_dir():
raise OSError(f"Path exists but is not a directory: {result_path}")
-
+
# Test write permissions by creating a temporary file
test_file = result_path / ".write_test"
try:
test_file.touch()
test_file.unlink() # Remove test file
except (PermissionError, OSError) as e:
- raise PermissionError(f"Directory created but not writable: {result_path}") from e
-
+ raise PermissionError(
+ f"Directory created but not writable: {result_path}"
+ ) from e
+
return True
-
+
except PermissionError:
raise
except OSError as e:
@@ -156,43 +155,42 @@ def create_result_directory(
def validate_result_directory_path(
- result_dir: str,
- check_parent_writable: bool = True
+ result_dir: str, check_parent_writable: bool = True
) -> None:
"""Validate that a result directory path is valid and can be created.
-
+
Performs validation without actually creating the directory.
-
+
Args:
result_dir: Path to validate
check_parent_writable: Whether to check if parent directory is writable
-
+
Raises:
ValueError: If path is invalid or unsafe
PermissionError: If parent directory is not writable
OSError: If path validation fails
-
+
Example:
>>> validate_result_directory_path("/path/to/results")
>>> # No exception means path is valid
"""
if not result_dir or not result_dir.strip():
raise ValueError("result_dir cannot be empty")
-
+
result_path = Path(result_dir.strip())
-
+
# Check for unsafe path components
try:
result_path.resolve()
except (OSError, RuntimeError) as e:
raise ValueError(f"Invalid path: {result_dir}") from e
-
+
# Check if path already exists
if result_path.exists():
if not result_path.is_dir():
raise OSError(f"Path exists but is not a directory: {result_path}")
return # Directory exists and is valid
-
+
# Check parent directory
parent_path = result_path.parent
if not parent_path.exists():
@@ -205,38 +203,37 @@ def validate_result_directory_path(
test_file.touch()
test_file.unlink()
except (PermissionError, OSError) as e:
- raise PermissionError(f"Parent directory not writable: {parent_path}") from e
+ raise PermissionError(
+ f"Parent directory not writable: {parent_path}"
+ ) from e
-def ensure_result_directory(
- result_dir: str,
- validate_first: bool = True
-) -> str:
+def ensure_result_directory(result_dir: str, validate_first: bool = True) -> str:
"""Ensure result directory exists, creating it if necessary.
-
+
This is a convenience function that combines validation and creation.
-
+
Args:
result_dir: Path to the result directory
validate_first: Whether to validate before creating
-
+
Returns:
Absolute path to the result directory
-
+
Raises:
ValueError: If path is invalid
PermissionError: If directory cannot be created due to permissions
OSError: If directory creation fails
-
+
Example:
>>> path = ensure_result_directory("/path/to/results")
>>> print(path) # "/absolute/path/to/results"
"""
if validate_first:
validate_result_directory_path(result_dir)
-
+
success = create_result_directory(result_dir)
if not success:
raise OSError(f"Failed to create result directory: {result_dir}")
-
- return str(Path(result_dir).resolve())
\ No newline at end of file
+
+ return str(Path(result_dir).resolve())
diff --git a/benchmark/zero_scrolls/create_huggingface_dataset.py b/benchmark/zero_scrolls/create_huggingface_dataset.py
index 7e3e9d45..997f98d4 100644
--- a/benchmark/zero_scrolls/create_huggingface_dataset.py
+++ b/benchmark/zero_scrolls/create_huggingface_dataset.py
@@ -22,8 +22,12 @@
for task, max_new_tokens in MAX_NEW_TOKENS.items():
df = load_dataset("tau/zero_scrolls", task, split="test").to_pandas()
df["context"] = df.apply(lambda x: x["input"][: x["document_end_index"]], axis=1)
- df["question"] = df.apply(lambda x: x["input"][x["document_end_index"] : x["query_end_index"]], axis=1)
- df["answer_prefix"] = df.apply(lambda x: x["input"][x["query_end_index"] :], axis=1).str.strip()
+ df["question"] = df.apply(
+ lambda x: x["input"][x["document_end_index"] : x["query_end_index"]], axis=1
+ )
+ df["answer_prefix"] = df.apply(
+ lambda x: x["input"][x["query_end_index"] :], axis=1
+ ).str.strip()
df["answer"] = ""
df["task"] = task
df["max_new_tokens"] = max_new_tokens
diff --git a/benchmark/zero_scrolls/zero_scrolls.py b/benchmark/zero_scrolls/zero_scrolls.py
index 95993d03..c178d2a7 100644
--- a/benchmark/zero_scrolls/zero_scrolls.py
+++ b/benchmark/zero_scrolls/zero_scrolls.py
@@ -40,46 +40,61 @@ class ZeroScrolls(Benchmark):
# "book_sum_sort"
# ]
# all tasks are clubbed into a single subset on huggingface
- all_datasets: List[str] = [ "default"]
-
+ all_datasets: List[str] = ["default"]
+
benchmark_name: str = "zero_scrolls"
huggingface_dataset_id: str = "simonjegou/zero_scrolls"
def _load_datasets(self) -> pd.DataFrame:
"""Load ZeroScrolls datasets by individual configs.
-
+
ZeroScrolls requires loading each subset as a separate config.
-
+
Returns:
Combined pandas DataFrame with all samples from subsets_to_run.
"""
print(f"Loading ZeroScrolls datasets: {self.subsets_to_run}")
dfs = []
-
+
for subset in self.subsets_to_run:
try:
from datasets import load_dataset
- subset_dataset = load_dataset(self.huggingface_dataset_id, subset, split="test")
+
+ subset_dataset = load_dataset(
+ self.huggingface_dataset_id, subset, split="test"
+ )
subset_df = subset_dataset.to_pandas()
-
+
# Process the data according to ZeroScrolls format
- subset_df["context"] = subset_df.apply(lambda x: x["input"][: x["document_end_index"]], axis=1)
- subset_df["question"] = subset_df.apply(lambda x: x["input"][x["document_end_index"] : x["query_end_index"]], axis=1)
- subset_df["answer_prefix"] = subset_df.apply(lambda x: x["input"][x["query_end_index"] :], axis=1).str.strip()
- subset_df["answer"] = "" # ZeroScrolls doesn't provide ground truth answers
+ subset_df["context"] = subset_df.apply(
+ lambda x: x["input"][: x["document_end_index"]], axis=1
+ )
+ subset_df["question"] = subset_df.apply(
+ lambda x: x["input"][
+ x["document_end_index"] : x["query_end_index"]
+ ],
+ axis=1,
+ )
+ subset_df["answer_prefix"] = subset_df.apply(
+ lambda x: x["input"][x["query_end_index"] :], axis=1
+ ).str.strip()
+ subset_df["answer"] = (
+ "" # ZeroScrolls doesn't provide ground truth answers
+ )
subset_df["task"] = subset
-
+
dfs.append(subset_df)
print(f" โ Loaded {len(subset_df)} samples from {subset}")
except Exception as subset_error:
print(f" โ Failed to load {subset}: {str(subset_error)}")
continue
-
+
if not dfs:
raise Exception("No ZeroScrolls subsets could be loaded successfully")
-
+
# Combine all subset DataFrames
import pandas as pd
+
combined_df = pd.concat(dfs, ignore_index=True)
print(f"Combined {len(combined_df)} total samples from {len(dfs)} subsets")
return combined_df
@@ -104,23 +119,25 @@ def post_run_evaluate(self, results_df: pd.DataFrame) -> Dict[str, Any]:
# Group results by task
task_groups = results_df.groupby("task")
task_stats: Dict[str, Dict[str, Any]] = {}
-
+
for task_name, task_df in task_groups:
# Calculate basic statistics since no ground truth is available
- avg_length = task_df["predicted_answer"].str.len().mean() if len(task_df) > 0 else 0
+ avg_length = (
+ task_df["predicted_answer"].str.len().mean() if len(task_df) > 0 else 0
+ )
task_stats[task_name] = {
"num_samples": len(task_df),
"avg_response_length": round(avg_length, 2),
- "note": "No ground truth available for evaluation"
+ "note": "No ground truth available for evaluation",
}
-
+
overall_metrics: Dict[str, Any] = {
"task_stats": task_stats,
"summary": {
"total_tasks": len(task_stats),
"total_samples": len(results_df),
- "note": "ZeroScrolls benchmark completed. No ground truth available for scoring."
- }
+ "note": "ZeroScrolls benchmark completed. No ground truth available for scoring.",
+ },
}
-
- return overall_metrics
+
+ return overall_metrics
diff --git a/cursor_chats/plans/sample_benchmark_configs.py b/cursor_chats/plans/sample_benchmark_configs.py
index c3d16b0a..3c51005b 100644
--- a/cursor_chats/plans/sample_benchmark_configs.py
+++ b/cursor_chats/plans/sample_benchmark_configs.py
@@ -10,56 +10,35 @@
# 1. InfiniteBench - using passkey task
infinite_bench_config = BenchmarkConfig(
- benchmark_name="infinite_bench",
- subsets=["passkey"]
+ benchmark_name="infinite_bench", subsets=["passkey"]
)
# 2. Ruler - using 4096 context length
-ruler_config = BenchmarkConfig(
- benchmark_name="ruler",
- subsets=["4096"]
-)
+ruler_config = BenchmarkConfig(benchmark_name="ruler", subsets=["4096"])
# 3. Loogle - using shortdep_qa task
-loogle_config = BenchmarkConfig(
- benchmark_name="loogle",
- subsets=["shortdep_qa"]
-)
+loogle_config = BenchmarkConfig(benchmark_name="loogle", subsets=["shortdep_qa"])
# 4. ZeroScrolls - using gov_report task
zero_scrolls_config = BenchmarkConfig(
- benchmark_name="zero_scrolls",
- subsets=["gov_report"]
+ benchmark_name="zero_scrolls", subsets=["gov_report"]
)
# 5. LongBenchv2 - using 0shot task
-longbenchv2_config = BenchmarkConfig(
- benchmark_name="longbenchv2",
- subsets=["0shot"]
-)
+longbenchv2_config = BenchmarkConfig(benchmark_name="longbenchv2", subsets=["0shot"])
# 6. AIME2024 - using single task
-aime2024_config = BenchmarkConfig(
- benchmark_name="aime2024",
- subsets=["aime2024"]
-)
+aime2024_config = BenchmarkConfig(benchmark_name="aime2024", subsets=["aime2024"])
# 7. AIME2025 - using single task
-aime2025_config = BenchmarkConfig(
- benchmark_name="aime2025",
- subsets=["aime2025"]
-)
+aime2025_config = BenchmarkConfig(benchmark_name="aime2025", subsets=["aime2025"])
# 8. LongBench (existing) - using narrativeqa task
-longbench_config = BenchmarkConfig(
- benchmark_name="longbench",
- subsets=["narrativeqa"]
-)
+longbench_config = BenchmarkConfig(benchmark_name="longbench", subsets=["narrativeqa"])
# 9. Mock Benchmark (existing) - using single task
mock_benchmark_config = BenchmarkConfig(
- benchmark_name="mock_benchmark",
- subsets=["mock_task"]
+ benchmark_name="mock_benchmark", subsets=["mock_task"]
)
# List of all sample configurations
@@ -72,33 +51,35 @@
aime2024_config,
aime2025_config,
longbench_config,
- mock_benchmark_config
+ mock_benchmark_config,
]
+
# Example usage with executor
def get_sample_configs():
"""Return all sample benchmark configurations.
-
+
Returns:
List of BenchmarkConfig instances, one for each benchmark with a single subset.
"""
return all_sample_configs
+
# Example of how to use these configurations
if __name__ == "__main__":
print("Sample BenchmarkConfigs for all benchmarks:")
print("=" * 50)
-
+
for config in all_sample_configs:
print(f"Benchmark: {config.benchmark_name}")
print(f"Subsets: {config.subsets}")
print("-" * 30)
-
+
print(f"\nTotal configurations: {len(all_sample_configs)}")
-
+
# Example of validation
from benchmark.benchmark_registry import create_benchmark_instance
-
+
print("\nValidating configurations...")
for config in all_sample_configs:
try:
@@ -106,4 +87,4 @@ def get_sample_configs():
config.validate_with_benchmark_instance(benchmark)
print(f"โ
{config.benchmark_name}: Valid")
except Exception as e:
- print(f"โ {config.benchmark_name}: {str(e)}")
\ No newline at end of file
+ print(f"โ {config.benchmark_name}: {str(e)}")
diff --git a/docs/logo.png b/docs/logo.png
new file mode 100644
index 00000000..f4be273d
Binary files /dev/null and b/docs/logo.png differ
diff --git a/poetry.lock b/poetry.lock
new file mode 100644
index 00000000..c5ecf7b3
--- /dev/null
+++ b/poetry.lock
@@ -0,0 +1,5792 @@
+# This file is automatically @generated by Poetry 2.1.3 and should not be changed by hand.
+
+[[package]]
+name = "aiohappyeyeballs"
+version = "2.6.1"
+description = "Happy Eyeballs for asyncio"
+optional = false
+python-versions = ">=3.9"
+groups = ["main"]
+files = [
+ {file = "aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8"},
+ {file = "aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558"},
+]
+
+[[package]]
+name = "aiohttp"
+version = "3.12.15"
+description = "Async http client/server framework (asyncio)"
+optional = false
+python-versions = ">=3.9"
+groups = ["main"]
+files = [
+ {file = "aiohttp-3.12.15-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b6fc902bff74d9b1879ad55f5404153e2b33a82e72a95c89cec5eb6cc9e92fbc"},
+ {file = "aiohttp-3.12.15-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:098e92835b8119b54c693f2f88a1dec690e20798ca5f5fe5f0520245253ee0af"},
+ {file = "aiohttp-3.12.15-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:40b3fee496a47c3b4a39a731954c06f0bd9bd3e8258c059a4beb76ac23f8e421"},
+ {file = "aiohttp-3.12.15-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ce13fcfb0bb2f259fb42106cdc63fa5515fb85b7e87177267d89a771a660b79"},
+ {file = "aiohttp-3.12.15-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3beb14f053222b391bf9cf92ae82e0171067cc9c8f52453a0f1ec7c37df12a77"},
+ {file = "aiohttp-3.12.15-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4c39e87afe48aa3e814cac5f535bc6199180a53e38d3f51c5e2530f5aa4ec58c"},
+ {file = "aiohttp-3.12.15-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5f1b4ce5bc528a6ee38dbf5f39bbf11dd127048726323b72b8e85769319ffc4"},
+ {file = "aiohttp-3.12.15-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1004e67962efabbaf3f03b11b4c43b834081c9e3f9b32b16a7d97d4708a9abe6"},
+ {file = "aiohttp-3.12.15-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8faa08fcc2e411f7ab91d1541d9d597d3a90e9004180edb2072238c085eac8c2"},
+ {file = "aiohttp-3.12.15-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:fe086edf38b2222328cdf89af0dde2439ee173b8ad7cb659b4e4c6f385b2be3d"},
+ {file = "aiohttp-3.12.15-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:79b26fe467219add81d5e47b4a4ba0f2394e8b7c7c3198ed36609f9ba161aecb"},
+ {file = "aiohttp-3.12.15-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b761bac1192ef24e16706d761aefcb581438b34b13a2f069a6d343ec8fb693a5"},
+ {file = "aiohttp-3.12.15-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:e153e8adacfe2af562861b72f8bc47f8a5c08e010ac94eebbe33dc21d677cd5b"},
+ {file = "aiohttp-3.12.15-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:fc49c4de44977aa8601a00edbf157e9a421f227aa7eb477d9e3df48343311065"},
+ {file = "aiohttp-3.12.15-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2776c7ec89c54a47029940177e75c8c07c29c66f73464784971d6a81904ce9d1"},
+ {file = "aiohttp-3.12.15-cp310-cp310-win32.whl", hash = "sha256:2c7d81a277fa78b2203ab626ced1487420e8c11a8e373707ab72d189fcdad20a"},
+ {file = "aiohttp-3.12.15-cp310-cp310-win_amd64.whl", hash = "sha256:83603f881e11f0f710f8e2327817c82e79431ec976448839f3cd05d7afe8f830"},
+ {file = "aiohttp-3.12.15-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d3ce17ce0220383a0f9ea07175eeaa6aa13ae5a41f30bc61d84df17f0e9b1117"},
+ {file = "aiohttp-3.12.15-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:010cc9bbd06db80fe234d9003f67e97a10fe003bfbedb40da7d71c1008eda0fe"},
+ {file = "aiohttp-3.12.15-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3f9d7c55b41ed687b9d7165b17672340187f87a773c98236c987f08c858145a9"},
+ {file = "aiohttp-3.12.15-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc4fbc61bb3548d3b482f9ac7ddd0f18c67e4225aaa4e8552b9f1ac7e6bda9e5"},
+ {file = "aiohttp-3.12.15-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7fbc8a7c410bb3ad5d595bb7118147dfbb6449d862cc1125cf8867cb337e8728"},
+ {file = "aiohttp-3.12.15-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:74dad41b3458dbb0511e760fb355bb0b6689e0630de8a22b1b62a98777136e16"},
+ {file = "aiohttp-3.12.15-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b6f0af863cf17e6222b1735a756d664159e58855da99cfe965134a3ff63b0b0"},
+ {file = "aiohttp-3.12.15-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b5b7fe4972d48a4da367043b8e023fb70a04d1490aa7d68800e465d1b97e493b"},
+ {file = "aiohttp-3.12.15-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6443cca89553b7a5485331bc9bedb2342b08d073fa10b8c7d1c60579c4a7b9bd"},
+ {file = "aiohttp-3.12.15-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6c5f40ec615e5264f44b4282ee27628cea221fcad52f27405b80abb346d9f3f8"},
+ {file = "aiohttp-3.12.15-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:2abbb216a1d3a2fe86dbd2edce20cdc5e9ad0be6378455b05ec7f77361b3ab50"},
+ {file = "aiohttp-3.12.15-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:db71ce547012a5420a39c1b744d485cfb823564d01d5d20805977f5ea1345676"},
+ {file = "aiohttp-3.12.15-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:ced339d7c9b5030abad5854aa5413a77565e5b6e6248ff927d3e174baf3badf7"},
+ {file = "aiohttp-3.12.15-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:7c7dd29c7b5bda137464dc9bfc738d7ceea46ff70309859ffde8c022e9b08ba7"},
+ {file = "aiohttp-3.12.15-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:421da6fd326460517873274875c6c5a18ff225b40da2616083c5a34a7570b685"},
+ {file = "aiohttp-3.12.15-cp311-cp311-win32.whl", hash = "sha256:4420cf9d179ec8dfe4be10e7d0fe47d6d606485512ea2265b0d8c5113372771b"},
+ {file = "aiohttp-3.12.15-cp311-cp311-win_amd64.whl", hash = "sha256:edd533a07da85baa4b423ee8839e3e91681c7bfa19b04260a469ee94b778bf6d"},
+ {file = "aiohttp-3.12.15-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:802d3868f5776e28f7bf69d349c26fc0efadb81676d0afa88ed00d98a26340b7"},
+ {file = "aiohttp-3.12.15-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f2800614cd560287be05e33a679638e586a2d7401f4ddf99e304d98878c29444"},
+ {file = "aiohttp-3.12.15-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8466151554b593909d30a0a125d638b4e5f3836e5aecde85b66b80ded1cb5b0d"},
+ {file = "aiohttp-3.12.15-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e5a495cb1be69dae4b08f35a6c4579c539e9b5706f606632102c0f855bcba7c"},
+ {file = "aiohttp-3.12.15-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6404dfc8cdde35c69aaa489bb3542fb86ef215fc70277c892be8af540e5e21c0"},
+ {file = "aiohttp-3.12.15-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3ead1c00f8521a5c9070fcb88f02967b1d8a0544e6d85c253f6968b785e1a2ab"},
+ {file = "aiohttp-3.12.15-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6990ef617f14450bc6b34941dba4f12d5613cbf4e33805932f853fbd1cf18bfb"},
+ {file = "aiohttp-3.12.15-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd736ed420f4db2b8148b52b46b88ed038d0354255f9a73196b7bbce3ea97545"},
+ {file = "aiohttp-3.12.15-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c5092ce14361a73086b90c6efb3948ffa5be2f5b6fbcf52e8d8c8b8848bb97c"},
+ {file = "aiohttp-3.12.15-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:aaa2234bb60c4dbf82893e934d8ee8dea30446f0647e024074237a56a08c01bd"},
+ {file = "aiohttp-3.12.15-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:6d86a2fbdd14192e2f234a92d3b494dd4457e683ba07e5905a0b3ee25389ac9f"},
+ {file = "aiohttp-3.12.15-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a041e7e2612041a6ddf1c6a33b883be6a421247c7afd47e885969ee4cc58bd8d"},
+ {file = "aiohttp-3.12.15-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5015082477abeafad7203757ae44299a610e89ee82a1503e3d4184e6bafdd519"},
+ {file = "aiohttp-3.12.15-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:56822ff5ddfd1b745534e658faba944012346184fbfe732e0d6134b744516eea"},
+ {file = "aiohttp-3.12.15-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b2acbbfff69019d9014508c4ba0401822e8bae5a5fdc3b6814285b71231b60f3"},
+ {file = "aiohttp-3.12.15-cp312-cp312-win32.whl", hash = "sha256:d849b0901b50f2185874b9a232f38e26b9b3d4810095a7572eacea939132d4e1"},
+ {file = "aiohttp-3.12.15-cp312-cp312-win_amd64.whl", hash = "sha256:b390ef5f62bb508a9d67cb3bba9b8356e23b3996da7062f1a57ce1a79d2b3d34"},
+ {file = "aiohttp-3.12.15-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:9f922ffd05034d439dde1c77a20461cf4a1b0831e6caa26151fe7aa8aaebc315"},
+ {file = "aiohttp-3.12.15-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2ee8a8ac39ce45f3e55663891d4b1d15598c157b4d494a4613e704c8b43112cd"},
+ {file = "aiohttp-3.12.15-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3eae49032c29d356b94eee45a3f39fdf4b0814b397638c2f718e96cfadf4c4e4"},
+ {file = "aiohttp-3.12.15-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b97752ff12cc12f46a9b20327104448042fce5c33a624f88c18f66f9368091c7"},
+ {file = "aiohttp-3.12.15-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:894261472691d6fe76ebb7fcf2e5870a2ac284c7406ddc95823c8598a1390f0d"},
+ {file = "aiohttp-3.12.15-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5fa5d9eb82ce98959fc1031c28198b431b4d9396894f385cb63f1e2f3f20ca6b"},
+ {file = "aiohttp-3.12.15-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0fa751efb11a541f57db59c1dd821bec09031e01452b2b6217319b3a1f34f3d"},
+ {file = "aiohttp-3.12.15-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5346b93e62ab51ee2a9d68e8f73c7cf96ffb73568a23e683f931e52450e4148d"},
+ {file = "aiohttp-3.12.15-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:049ec0360f939cd164ecbfd2873eaa432613d5e77d6b04535e3d1fbae5a9e645"},
+ {file = "aiohttp-3.12.15-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b52dcf013b57464b6d1e51b627adfd69a8053e84b7103a7cd49c030f9ca44461"},
+ {file = "aiohttp-3.12.15-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:9b2af240143dd2765e0fb661fd0361a1b469cab235039ea57663cda087250ea9"},
+ {file = "aiohttp-3.12.15-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ac77f709a2cde2cc71257ab2d8c74dd157c67a0558a0d2799d5d571b4c63d44d"},
+ {file = "aiohttp-3.12.15-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:47f6b962246f0a774fbd3b6b7be25d59b06fdb2f164cf2513097998fc6a29693"},
+ {file = "aiohttp-3.12.15-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:760fb7db442f284996e39cf9915a94492e1896baac44f06ae551974907922b64"},
+ {file = "aiohttp-3.12.15-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ad702e57dc385cae679c39d318def49aef754455f237499d5b99bea4ef582e51"},
+ {file = "aiohttp-3.12.15-cp313-cp313-win32.whl", hash = "sha256:f813c3e9032331024de2eb2e32a88d86afb69291fbc37a3a3ae81cc9917fb3d0"},
+ {file = "aiohttp-3.12.15-cp313-cp313-win_amd64.whl", hash = "sha256:1a649001580bdb37c6fdb1bebbd7e3bc688e8ec2b5c6f52edbb664662b17dc84"},
+ {file = "aiohttp-3.12.15-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:691d203c2bdf4f4637792efbbcdcd157ae11e55eaeb5e9c360c1206fb03d4d98"},
+ {file = "aiohttp-3.12.15-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8e995e1abc4ed2a454c731385bf4082be06f875822adc4c6d9eaadf96e20d406"},
+ {file = "aiohttp-3.12.15-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:bd44d5936ab3193c617bfd6c9a7d8d1085a8dc8c3f44d5f1dcf554d17d04cf7d"},
+ {file = "aiohttp-3.12.15-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:46749be6e89cd78d6068cdf7da51dbcfa4321147ab8e4116ee6678d9a056a0cf"},
+ {file = "aiohttp-3.12.15-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0c643f4d75adea39e92c0f01b3fb83d57abdec8c9279b3078b68a3a52b3933b6"},
+ {file = "aiohttp-3.12.15-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0a23918fedc05806966a2438489dcffccbdf83e921a1170773b6178d04ade142"},
+ {file = "aiohttp-3.12.15-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:74bdd8c864b36c3673741023343565d95bfbd778ffe1eb4d412c135a28a8dc89"},
+ {file = "aiohttp-3.12.15-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a146708808c9b7a988a4af3821379e379e0f0e5e466ca31a73dbdd0325b0263"},
+ {file = "aiohttp-3.12.15-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7011a70b56facde58d6d26da4fec3280cc8e2a78c714c96b7a01a87930a9530"},
+ {file = "aiohttp-3.12.15-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:3bdd6e17e16e1dbd3db74d7f989e8af29c4d2e025f9828e6ef45fbdee158ec75"},
+ {file = "aiohttp-3.12.15-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:57d16590a351dfc914670bd72530fd78344b885a00b250e992faea565b7fdc05"},
+ {file = "aiohttp-3.12.15-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:bc9a0f6569ff990e0bbd75506c8d8fe7214c8f6579cca32f0546e54372a3bb54"},
+ {file = "aiohttp-3.12.15-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:536ad7234747a37e50e7b6794ea868833d5220b49c92806ae2d7e8a9d6b5de02"},
+ {file = "aiohttp-3.12.15-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:f0adb4177fa748072546fb650d9bd7398caaf0e15b370ed3317280b13f4083b0"},
+ {file = "aiohttp-3.12.15-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:14954a2988feae3987f1eb49c706bff39947605f4b6fa4027c1d75743723eb09"},
+ {file = "aiohttp-3.12.15-cp39-cp39-win32.whl", hash = "sha256:b784d6ed757f27574dca1c336f968f4e81130b27595e458e69457e6878251f5d"},
+ {file = "aiohttp-3.12.15-cp39-cp39-win_amd64.whl", hash = "sha256:86ceded4e78a992f835209e236617bffae649371c4a50d5e5a3987f237db84b8"},
+ {file = "aiohttp-3.12.15.tar.gz", hash = "sha256:4fc61385e9c98d72fcdf47e6dd81833f47b2f77c114c29cd64a361be57a763a2"},
+]
+
+[package.dependencies]
+aiohappyeyeballs = ">=2.5.0"
+aiosignal = ">=1.4.0"
+async-timeout = {version = ">=4.0,<6.0", markers = "python_version < \"3.11\""}
+attrs = ">=17.3.0"
+frozenlist = ">=1.1.1"
+multidict = ">=4.5,<7.0"
+propcache = ">=0.2.0"
+yarl = ">=1.17.0,<2.0"
+
+[package.extras]
+speedups = ["Brotli ; platform_python_implementation == \"CPython\"", "aiodns (>=3.3.0)", "brotlicffi ; platform_python_implementation != \"CPython\""]
+
+[[package]]
+name = "aiosignal"
+version = "1.4.0"
+description = "aiosignal: a list of registered asynchronous callbacks"
+optional = false
+python-versions = ">=3.9"
+groups = ["main"]
+files = [
+ {file = "aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e"},
+ {file = "aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7"},
+]
+
+[package.dependencies]
+frozenlist = ">=1.1.0"
+typing-extensions = {version = ">=4.2", markers = "python_version < \"3.13\""}
+
+[[package]]
+name = "alabaster"
+version = "0.7.16"
+description = "A light, configurable Sphinx theme"
+optional = false
+python-versions = ">=3.9"
+groups = ["docs"]
+markers = "python_version < \"3.11\""
+files = [
+ {file = "alabaster-0.7.16-py3-none-any.whl", hash = "sha256:b46733c07dce03ae4e150330b975c75737fa60f0a7c591b6c8bf4928a28e2c92"},
+ {file = "alabaster-0.7.16.tar.gz", hash = "sha256:75a8b99c28a5dad50dd7f8ccdd447a121ddb3892da9e53d1ca5cca3106d58d65"},
+]
+
+[[package]]
+name = "alabaster"
+version = "1.0.0"
+description = "A light, configurable Sphinx theme"
+optional = false
+python-versions = ">=3.10"
+groups = ["docs"]
+markers = "python_version >= \"3.11\""
+files = [
+ {file = "alabaster-1.0.0-py3-none-any.whl", hash = "sha256:fc6786402dc3fcb2de3cabd5fe455a2db534b371124f1f21de8731783dec828b"},
+ {file = "alabaster-1.0.0.tar.gz", hash = "sha256:c00dca57bca26fa62a6d7d0a9fcce65f3e026e9bfe33e9c538fd3fbb2144fd9e"},
+]
+
+[[package]]
+name = "annotated-types"
+version = "0.7.0"
+description = "Reusable constraint types to use with typing.Annotated"
+optional = false
+python-versions = ">=3.8"
+groups = ["benchmarks"]
+files = [
+ {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"},
+ {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"},
+]
+
+[[package]]
+name = "astroid"
+version = "2.15.8"
+description = "An abstract syntax tree for Python with inference support."
+optional = false
+python-versions = ">=3.7.2"
+groups = ["dev"]
+files = [
+ {file = "astroid-2.15.8-py3-none-any.whl", hash = "sha256:1aa149fc5c6589e3d0ece885b4491acd80af4f087baafa3fb5203b113e68cd3c"},
+ {file = "astroid-2.15.8.tar.gz", hash = "sha256:6c107453dffee9055899705de3c9ead36e74119cee151e5a9aaf7f0b0e020a6a"},
+]
+
+[package.dependencies]
+lazy-object-proxy = ">=1.4.0"
+typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.11\""}
+wrapt = [
+ {version = ">=1.11,<2", markers = "python_version < \"3.11\""},
+ {version = ">=1.14,<2", markers = "python_version >= \"3.11\""},
+]
+
+[[package]]
+name = "async-timeout"
+version = "5.0.1"
+description = "Timeout context manager for asyncio programs"
+optional = false
+python-versions = ">=3.8"
+groups = ["main"]
+markers = "python_version < \"3.11\""
+files = [
+ {file = "async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c"},
+ {file = "async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3"},
+]
+
+[[package]]
+name = "attrs"
+version = "25.3.0"
+description = "Classes Without Boilerplate"
+optional = false
+python-versions = ">=3.8"
+groups = ["main"]
+files = [
+ {file = "attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3"},
+ {file = "attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b"},
+]
+
+[package.extras]
+benchmark = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"]
+cov = ["cloudpickle ; platform_python_implementation == \"CPython\"", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"]
+dev = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"]
+docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier"]
+tests = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"]
+tests-mypy = ["mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\""]
+
+[[package]]
+name = "babel"
+version = "2.17.0"
+description = "Internationalization utilities"
+optional = false
+python-versions = ">=3.8"
+groups = ["docs"]
+files = [
+ {file = "babel-2.17.0-py3-none-any.whl", hash = "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2"},
+ {file = "babel-2.17.0.tar.gz", hash = "sha256:0c54cffb19f690cdcc52a3b50bcbf71e07a808d1c80d549f2459b9d2cf0afb9d"},
+]
+
+[package.extras]
+dev = ["backports.zoneinfo ; python_version < \"3.9\"", "freezegun (>=1.0,<2.0)", "jinja2 (>=3.0)", "pytest (>=6.0)", "pytest-cov", "pytz", "setuptools", "tzdata ; sys_platform == \"win32\""]
+
+[[package]]
+name = "bandit"
+version = "1.8.6"
+description = "Security oriented static analyser for python code."
+optional = false
+python-versions = ">=3.9"
+groups = ["dev"]
+files = [
+ {file = "bandit-1.8.6-py3-none-any.whl", hash = "sha256:3348e934d736fcdb68b6aa4030487097e23a501adf3e7827b63658df464dddd0"},
+ {file = "bandit-1.8.6.tar.gz", hash = "sha256:dbfe9c25fc6961c2078593de55fd19f2559f9e45b99f1272341f5b95dea4e56b"},
+]
+
+[package.dependencies]
+colorama = {version = ">=0.3.9", markers = "platform_system == \"Windows\""}
+PyYAML = ">=5.3.1"
+rich = "*"
+stevedore = ">=1.20.0"
+
+[package.extras]
+baseline = ["GitPython (>=3.1.30)"]
+sarif = ["jschema-to-python (>=1.2.3)", "sarif-om (>=1.0.4)"]
+test = ["beautifulsoup4 (>=4.8.0)", "coverage (>=4.5.4)", "fixtures (>=3.0.0)", "flake8 (>=4.0.0)", "pylint (==1.9.4)", "stestr (>=2.5.0)", "testscenarios (>=0.5.0)", "testtools (>=2.3.0)"]
+toml = ["tomli (>=1.1.0) ; python_version < \"3.11\""]
+yaml = ["PyYAML"]
+
+[[package]]
+name = "bert-score"
+version = "0.3.13"
+description = "PyTorch implementation of BERT score"
+optional = false
+python-versions = ">=3.6"
+groups = ["main"]
+files = [
+ {file = "bert_score-0.3.13-py3-none-any.whl", hash = "sha256:bbbb4c7fcdaa46d7681aff49f37f96faa09ed74e1b150e659bdc6b58a66989b9"},
+ {file = "bert_score-0.3.13.tar.gz", hash = "sha256:8ffe5838eac8cdd988b8b1a896af7f49071188c8c011a1ed160d71a9899a2ba4"},
+]
+
+[package.dependencies]
+matplotlib = "*"
+numpy = "*"
+packaging = ">=20.9"
+pandas = ">=1.0.1"
+requests = "*"
+torch = ">=1.0.0"
+tqdm = ">=4.31.1"
+transformers = ">=3.0.0"
+
+[[package]]
+name = "black"
+version = "22.10.0"
+description = "The uncompromising code formatter."
+optional = false
+python-versions = ">=3.7"
+groups = ["dev"]
+files = [
+ {file = "black-22.10.0-1fixedarch-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:5cc42ca67989e9c3cf859e84c2bf014f6633db63d1cbdf8fdb666dcd9e77e3fa"},
+ {file = "black-22.10.0-1fixedarch-cp311-cp311-macosx_11_0_x86_64.whl", hash = "sha256:5d8f74030e67087b219b032aa33a919fae8806d49c867846bfacde57f43972ef"},
+ {file = "black-22.10.0-1fixedarch-cp37-cp37m-macosx_10_16_x86_64.whl", hash = "sha256:197df8509263b0b8614e1df1756b1dd41be6738eed2ba9e9769f3880c2b9d7b6"},
+ {file = "black-22.10.0-1fixedarch-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:2644b5d63633702bc2c5f3754b1b475378fbbfb481f62319388235d0cd104c2d"},
+ {file = "black-22.10.0-1fixedarch-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:e41a86c6c650bcecc6633ee3180d80a025db041a8e2398dcc059b3afa8382cd4"},
+ {file = "black-22.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2039230db3c6c639bd84efe3292ec7b06e9214a2992cd9beb293d639c6402edb"},
+ {file = "black-22.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14ff67aec0a47c424bc99b71005202045dc09270da44a27848d534600ac64fc7"},
+ {file = "black-22.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:819dc789f4498ecc91438a7de64427c73b45035e2e3680c92e18795a839ebb66"},
+ {file = "black-22.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5b9b29da4f564ba8787c119f37d174f2b69cdfdf9015b7d8c5c16121ddc054ae"},
+ {file = "black-22.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8b49776299fece66bffaafe357d929ca9451450f5466e997a7285ab0fe28e3b"},
+ {file = "black-22.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:21199526696b8f09c3997e2b4db8d0b108d801a348414264d2eb8eb2532e540d"},
+ {file = "black-22.10.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e464456d24e23d11fced2bc8c47ef66d471f845c7b7a42f3bd77bf3d1789650"},
+ {file = "black-22.10.0-cp37-cp37m-win_amd64.whl", hash = "sha256:9311e99228ae10023300ecac05be5a296f60d2fd10fff31cf5c1fa4ca4b1988d"},
+ {file = "black-22.10.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:fba8a281e570adafb79f7755ac8721b6cf1bbf691186a287e990c7929c7692ff"},
+ {file = "black-22.10.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:915ace4ff03fdfff953962fa672d44be269deb2eaf88499a0f8805221bc68c87"},
+ {file = "black-22.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:444ebfb4e441254e87bad00c661fe32df9969b2bf224373a448d8aca2132b395"},
+ {file = "black-22.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:974308c58d057a651d182208a484ce80a26dac0caef2895836a92dd6ebd725e0"},
+ {file = "black-22.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72ef3925f30e12a184889aac03d77d031056860ccae8a1e519f6cbb742736383"},
+ {file = "black-22.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:432247333090c8c5366e69627ccb363bc58514ae3e63f7fc75c54b1ea80fa7de"},
+ {file = "black-22.10.0-py3-none-any.whl", hash = "sha256:c957b2b4ea88587b46cf49d1dc17681c1e672864fd7af32fc1e9664d572b3458"},
+ {file = "black-22.10.0.tar.gz", hash = "sha256:f513588da599943e0cde4e32cc9879e825d58720d6557062d1098c5ad80080e1"},
+]
+
+[package.dependencies]
+click = ">=8.0.0"
+mypy-extensions = ">=0.4.3"
+pathspec = ">=0.9.0"
+platformdirs = ">=2"
+tomli = {version = ">=1.1.0", markers = "python_full_version < \"3.11.0a7\""}
+typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""}
+
+[package.extras]
+colorama = ["colorama (>=0.4.3)"]
+d = ["aiohttp (>=3.7.4)"]
+jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"]
+uvloop = ["uvloop (>=0.15.2)"]
+
+[[package]]
+name = "certifi"
+version = "2025.8.3"
+description = "Python package for providing Mozilla's CA Bundle."
+optional = false
+python-versions = ">=3.7"
+groups = ["main", "benchmarks", "docs"]
+files = [
+ {file = "certifi-2025.8.3-py3-none-any.whl", hash = "sha256:f6c12493cfb1b06ba2ff328595af9350c65d6644968e5d3a2ffd78699af217a5"},
+ {file = "certifi-2025.8.3.tar.gz", hash = "sha256:e564105f78ded564e3ae7c923924435e1daa7463faeab5bb932bc53ffae63407"},
+]
+
+[[package]]
+name = "cfgv"
+version = "3.4.0"
+description = "Validate configuration and produce human readable error messages."
+optional = false
+python-versions = ">=3.8"
+groups = ["dev"]
+files = [
+ {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"},
+ {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"},
+]
+
+[[package]]
+name = "charset-normalizer"
+version = "3.4.3"
+description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
+optional = false
+python-versions = ">=3.7"
+groups = ["main", "benchmarks", "dev", "docs"]
+files = [
+ {file = "charset_normalizer-3.4.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fb7f67a1bfa6e40b438170ebdc8158b78dc465a5a67b6dde178a46987b244a72"},
+ {file = "charset_normalizer-3.4.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cc9370a2da1ac13f0153780040f465839e6cccb4a1e44810124b4e22483c93fe"},
+ {file = "charset_normalizer-3.4.3-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:07a0eae9e2787b586e129fdcbe1af6997f8d0e5abaa0bc98c0e20e124d67e601"},
+ {file = "charset_normalizer-3.4.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:74d77e25adda8581ffc1c720f1c81ca082921329452eba58b16233ab1842141c"},
+ {file = "charset_normalizer-3.4.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d0e909868420b7049dafd3a31d45125b31143eec59235311fc4c57ea26a4acd2"},
+ {file = "charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c6f162aabe9a91a309510d74eeb6507fab5fff92337a15acbe77753d88d9dcf0"},
+ {file = "charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4ca4c094de7771a98d7fbd67d9e5dbf1eb73efa4f744a730437d8a3a5cf994f0"},
+ {file = "charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:02425242e96bcf29a49711b0ca9f37e451da7c70562bc10e8ed992a5a7a25cc0"},
+ {file = "charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:78deba4d8f9590fe4dae384aeff04082510a709957e968753ff3c48399f6f92a"},
+ {file = "charset_normalizer-3.4.3-cp310-cp310-win32.whl", hash = "sha256:d79c198e27580c8e958906f803e63cddb77653731be08851c7df0b1a14a8fc0f"},
+ {file = "charset_normalizer-3.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:c6e490913a46fa054e03699c70019ab869e990270597018cef1d8562132c2669"},
+ {file = "charset_normalizer-3.4.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b256ee2e749283ef3ddcff51a675ff43798d92d746d1a6e4631bf8c707d22d0b"},
+ {file = "charset_normalizer-3.4.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:13faeacfe61784e2559e690fc53fa4c5ae97c6fcedb8eb6fb8d0a15b475d2c64"},
+ {file = "charset_normalizer-3.4.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:00237675befef519d9af72169d8604a067d92755e84fe76492fef5441db05b91"},
+ {file = "charset_normalizer-3.4.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:585f3b2a80fbd26b048a0be90c5aae8f06605d3c92615911c3a2b03a8a3b796f"},
+ {file = "charset_normalizer-3.4.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e78314bdc32fa80696f72fa16dc61168fda4d6a0c014e0380f9d02f0e5d8a07"},
+ {file = "charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:96b2b3d1a83ad55310de8c7b4a2d04d9277d5591f40761274856635acc5fcb30"},
+ {file = "charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:939578d9d8fd4299220161fdd76e86c6a251987476f5243e8864a7844476ba14"},
+ {file = "charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:fd10de089bcdcd1be95a2f73dbe6254798ec1bda9f450d5828c96f93e2536b9c"},
+ {file = "charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1e8ac75d72fa3775e0b7cb7e4629cec13b7514d928d15ef8ea06bca03ef01cae"},
+ {file = "charset_normalizer-3.4.3-cp311-cp311-win32.whl", hash = "sha256:6cf8fd4c04756b6b60146d98cd8a77d0cdae0e1ca20329da2ac85eed779b6849"},
+ {file = "charset_normalizer-3.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:31a9a6f775f9bcd865d88ee350f0ffb0e25936a7f930ca98995c05abf1faf21c"},
+ {file = "charset_normalizer-3.4.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e28e334d3ff134e88989d90ba04b47d84382a828c061d0d1027b1b12a62b39b1"},
+ {file = "charset_normalizer-3.4.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0cacf8f7297b0c4fcb74227692ca46b4a5852f8f4f24b3c766dd94a1075c4884"},
+ {file = "charset_normalizer-3.4.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c6fd51128a41297f5409deab284fecbe5305ebd7e5a1f959bee1c054622b7018"},
+ {file = "charset_normalizer-3.4.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3cfb2aad70f2c6debfbcb717f23b7eb55febc0bb23dcffc0f076009da10c6392"},
+ {file = "charset_normalizer-3.4.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1606f4a55c0fd363d754049cdf400175ee96c992b1f8018b993941f221221c5f"},
+ {file = "charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:027b776c26d38b7f15b26a5da1044f376455fb3766df8fc38563b4efbc515154"},
+ {file = "charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:42e5088973e56e31e4fa58eb6bd709e42fc03799c11c42929592889a2e54c491"},
+ {file = "charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cc34f233c9e71701040d772aa7490318673aa7164a0efe3172b2981218c26d93"},
+ {file = "charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:320e8e66157cc4e247d9ddca8e21f427efc7a04bbd0ac8a9faf56583fa543f9f"},
+ {file = "charset_normalizer-3.4.3-cp312-cp312-win32.whl", hash = "sha256:fb6fecfd65564f208cbf0fba07f107fb661bcd1a7c389edbced3f7a493f70e37"},
+ {file = "charset_normalizer-3.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:86df271bf921c2ee3818f0522e9a5b8092ca2ad8b065ece5d7d9d0e9f4849bcc"},
+ {file = "charset_normalizer-3.4.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:14c2a87c65b351109f6abfc424cab3927b3bdece6f706e4d12faaf3d52ee5efe"},
+ {file = "charset_normalizer-3.4.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:41d1fc408ff5fdfb910200ec0e74abc40387bccb3252f3f27c0676731df2b2c8"},
+ {file = "charset_normalizer-3.4.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1bb60174149316da1c35fa5233681f7c0f9f514509b8e399ab70fea5f17e45c9"},
+ {file = "charset_normalizer-3.4.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:30d006f98569de3459c2fc1f2acde170b7b2bd265dc1943e87e1a4efe1b67c31"},
+ {file = "charset_normalizer-3.4.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:416175faf02e4b0810f1f38bcb54682878a4af94059a1cd63b8747244420801f"},
+ {file = "charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6aab0f181c486f973bc7262a97f5aca3ee7e1437011ef0c2ec04b5a11d16c927"},
+ {file = "charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:fdabf8315679312cfa71302f9bd509ded4f2f263fb5b765cf1433b39106c3cc9"},
+ {file = "charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:bd28b817ea8c70215401f657edef3a8aa83c29d447fb0b622c35403780ba11d5"},
+ {file = "charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:18343b2d246dc6761a249ba1fb13f9ee9a2bcd95decc767319506056ea4ad4dc"},
+ {file = "charset_normalizer-3.4.3-cp313-cp313-win32.whl", hash = "sha256:6fb70de56f1859a3f71261cbe41005f56a7842cc348d3aeb26237560bfa5e0ce"},
+ {file = "charset_normalizer-3.4.3-cp313-cp313-win_amd64.whl", hash = "sha256:cf1ebb7d78e1ad8ec2a8c4732c7be2e736f6e5123a4146c5b89c9d1f585f8cef"},
+ {file = "charset_normalizer-3.4.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3cd35b7e8aedeb9e34c41385fda4f73ba609e561faedfae0a9e75e44ac558a15"},
+ {file = "charset_normalizer-3.4.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b89bc04de1d83006373429975f8ef9e7932534b8cc9ca582e4db7d20d91816db"},
+ {file = "charset_normalizer-3.4.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2001a39612b241dae17b4687898843f254f8748b796a2e16f1051a17078d991d"},
+ {file = "charset_normalizer-3.4.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8dcfc373f888e4fb39a7bc57e93e3b845e7f462dacc008d9749568b1c4ece096"},
+ {file = "charset_normalizer-3.4.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18b97b8404387b96cdbd30ad660f6407799126d26a39ca65729162fd810a99aa"},
+ {file = "charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ccf600859c183d70eb47e05a44cd80a4ce77394d1ac0f79dbd2dd90a69a3a049"},
+ {file = "charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:53cd68b185d98dde4ad8990e56a58dea83a4162161b1ea9272e5c9182ce415e0"},
+ {file = "charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:30a96e1e1f865f78b030d65241c1ee850cdf422d869e9028e2fc1d5e4db73b92"},
+ {file = "charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d716a916938e03231e86e43782ca7878fb602a125a91e7acb8b5112e2e96ac16"},
+ {file = "charset_normalizer-3.4.3-cp314-cp314-win32.whl", hash = "sha256:c6dbd0ccdda3a2ba7c2ecd9d77b37f3b5831687d8dc1b6ca5f56a4880cc7b7ce"},
+ {file = "charset_normalizer-3.4.3-cp314-cp314-win_amd64.whl", hash = "sha256:73dc19b562516fc9bcf6e5d6e596df0b4eb98d87e4f79f3ae71840e6ed21361c"},
+ {file = "charset_normalizer-3.4.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0f2be7e0cf7754b9a30eb01f4295cc3d4358a479843b31f328afd210e2c7598c"},
+ {file = "charset_normalizer-3.4.3-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c60e092517a73c632ec38e290eba714e9627abe9d301c8c8a12ec32c314a2a4b"},
+ {file = "charset_normalizer-3.4.3-cp38-cp38-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:252098c8c7a873e17dd696ed98bbe91dbacd571da4b87df3736768efa7a792e4"},
+ {file = "charset_normalizer-3.4.3-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3653fad4fe3ed447a596ae8638b437f827234f01a8cd801842e43f3d0a6b281b"},
+ {file = "charset_normalizer-3.4.3-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8999f965f922ae054125286faf9f11bc6932184b93011d138925a1773830bbe9"},
+ {file = "charset_normalizer-3.4.3-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d95bfb53c211b57198bb91c46dd5a2d8018b3af446583aab40074bf7988401cb"},
+ {file = "charset_normalizer-3.4.3-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:5b413b0b1bfd94dbf4023ad6945889f374cd24e3f62de58d6bb102c4d9ae534a"},
+ {file = "charset_normalizer-3.4.3-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:b5e3b2d152e74e100a9e9573837aba24aab611d39428ded46f4e4022ea7d1942"},
+ {file = "charset_normalizer-3.4.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:a2d08ac246bb48479170408d6c19f6385fa743e7157d716e144cad849b2dd94b"},
+ {file = "charset_normalizer-3.4.3-cp38-cp38-win32.whl", hash = "sha256:ec557499516fc90fd374bf2e32349a2887a876fbf162c160e3c01b6849eaf557"},
+ {file = "charset_normalizer-3.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:5d8d01eac18c423815ed4f4a2ec3b439d654e55ee4ad610e153cf02faf67ea40"},
+ {file = "charset_normalizer-3.4.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:70bfc5f2c318afece2f5838ea5e4c3febada0be750fcf4775641052bbba14d05"},
+ {file = "charset_normalizer-3.4.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:23b6b24d74478dc833444cbd927c338349d6ae852ba53a0d02a2de1fce45b96e"},
+ {file = "charset_normalizer-3.4.3-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:34a7f768e3f985abdb42841e20e17b330ad3aaf4bb7e7aeeb73db2e70f077b99"},
+ {file = "charset_normalizer-3.4.3-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fb731e5deb0c7ef82d698b0f4c5bb724633ee2a489401594c5c88b02e6cb15f7"},
+ {file = "charset_normalizer-3.4.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:257f26fed7d7ff59921b78244f3cd93ed2af1800ff048c33f624c87475819dd7"},
+ {file = "charset_normalizer-3.4.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1ef99f0456d3d46a50945c98de1774da86f8e992ab5c77865ea8b8195341fc19"},
+ {file = "charset_normalizer-3.4.3-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:2c322db9c8c89009a990ef07c3bcc9f011a3269bc06782f916cd3d9eed7c9312"},
+ {file = "charset_normalizer-3.4.3-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:511729f456829ef86ac41ca78c63a5cb55240ed23b4b737faca0eb1abb1c41bc"},
+ {file = "charset_normalizer-3.4.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:88ab34806dea0671532d3f82d82b85e8fc23d7b2dd12fa837978dad9bb392a34"},
+ {file = "charset_normalizer-3.4.3-cp39-cp39-win32.whl", hash = "sha256:16a8770207946ac75703458e2c743631c79c59c5890c80011d536248f8eaa432"},
+ {file = "charset_normalizer-3.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:d22dbedd33326a4a5190dd4fe9e9e693ef12160c77382d9e87919bce54f3d4ca"},
+ {file = "charset_normalizer-3.4.3-py3-none-any.whl", hash = "sha256:ce571ab16d890d23b5c278547ba694193a45011ff86a9162a71307ed9f86759a"},
+ {file = "charset_normalizer-3.4.3.tar.gz", hash = "sha256:6fce4b8500244f6fcb71465d4a4930d132ba9ab8e71a7859e6a5d59851068d14"},
+]
+
+[[package]]
+name = "click"
+version = "8.1.8"
+description = "Composable command line interface toolkit"
+optional = false
+python-versions = ">=3.7"
+groups = ["main", "benchmarks", "dev"]
+markers = "python_version < \"3.11\""
+files = [
+ {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"},
+ {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"},
+]
+
+[package.dependencies]
+colorama = {version = "*", markers = "platform_system == \"Windows\""}
+
+[[package]]
+name = "click"
+version = "8.2.1"
+description = "Composable command line interface toolkit"
+optional = false
+python-versions = ">=3.10"
+groups = ["main", "benchmarks", "dev"]
+markers = "python_version >= \"3.11\""
+files = [
+ {file = "click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b"},
+ {file = "click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202"},
+]
+
+[package.dependencies]
+colorama = {version = "*", markers = "platform_system == \"Windows\""}
+
+[[package]]
+name = "colorama"
+version = "0.4.6"
+description = "Cross-platform colored terminal text."
+optional = false
+python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
+groups = ["main", "benchmarks", "dev", "docs"]
+files = [
+ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
+ {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
+]
+
+[[package]]
+name = "contourpy"
+version = "1.3.0"
+description = "Python library for calculating contours of 2D quadrilateral grids"
+optional = false
+python-versions = ">=3.9"
+groups = ["main"]
+markers = "python_version < \"3.11\""
+files = [
+ {file = "contourpy-1.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:880ea32e5c774634f9fcd46504bf9f080a41ad855f4fef54f5380f5133d343c7"},
+ {file = "contourpy-1.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:76c905ef940a4474a6289c71d53122a4f77766eef23c03cd57016ce19d0f7b42"},
+ {file = "contourpy-1.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:92f8557cbb07415a4d6fa191f20fd9d2d9eb9c0b61d1b2f52a8926e43c6e9af7"},
+ {file = "contourpy-1.3.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:36f965570cff02b874773c49bfe85562b47030805d7d8360748f3eca570f4cab"},
+ {file = "contourpy-1.3.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cacd81e2d4b6f89c9f8a5b69b86490152ff39afc58a95af002a398273e5ce589"},
+ {file = "contourpy-1.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:69375194457ad0fad3a839b9e29aa0b0ed53bb54db1bfb6c3ae43d111c31ce41"},
+ {file = "contourpy-1.3.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7a52040312b1a858b5e31ef28c2e865376a386c60c0e248370bbea2d3f3b760d"},
+ {file = "contourpy-1.3.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3faeb2998e4fcb256542e8a926d08da08977f7f5e62cf733f3c211c2a5586223"},
+ {file = "contourpy-1.3.0-cp310-cp310-win32.whl", hash = "sha256:36e0cff201bcb17a0a8ecc7f454fe078437fa6bda730e695a92f2d9932bd507f"},
+ {file = "contourpy-1.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:87ddffef1dbe5e669b5c2440b643d3fdd8622a348fe1983fad7a0f0ccb1cd67b"},
+ {file = "contourpy-1.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0fa4c02abe6c446ba70d96ece336e621efa4aecae43eaa9b030ae5fb92b309ad"},
+ {file = "contourpy-1.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:834e0cfe17ba12f79963861e0f908556b2cedd52e1f75e6578801febcc6a9f49"},
+ {file = "contourpy-1.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dbc4c3217eee163fa3984fd1567632b48d6dfd29216da3ded3d7b844a8014a66"},
+ {file = "contourpy-1.3.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4865cd1d419e0c7a7bf6de1777b185eebdc51470800a9f42b9e9decf17762081"},
+ {file = "contourpy-1.3.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:303c252947ab4b14c08afeb52375b26781ccd6a5ccd81abcdfc1fafd14cf93c1"},
+ {file = "contourpy-1.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:637f674226be46f6ba372fd29d9523dd977a291f66ab2a74fbeb5530bb3f445d"},
+ {file = "contourpy-1.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:76a896b2f195b57db25d6b44e7e03f221d32fe318d03ede41f8b4d9ba1bff53c"},
+ {file = "contourpy-1.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e1fd23e9d01591bab45546c089ae89d926917a66dceb3abcf01f6105d927e2cb"},
+ {file = "contourpy-1.3.0-cp311-cp311-win32.whl", hash = "sha256:d402880b84df3bec6eab53cd0cf802cae6a2ef9537e70cf75e91618a3801c20c"},
+ {file = "contourpy-1.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:6cb6cc968059db9c62cb35fbf70248f40994dfcd7aa10444bbf8b3faeb7c2d67"},
+ {file = "contourpy-1.3.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:570ef7cf892f0afbe5b2ee410c507ce12e15a5fa91017a0009f79f7d93a1268f"},
+ {file = "contourpy-1.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:da84c537cb8b97d153e9fb208c221c45605f73147bd4cadd23bdae915042aad6"},
+ {file = "contourpy-1.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0be4d8425bfa755e0fd76ee1e019636ccc7c29f77a7c86b4328a9eb6a26d0639"},
+ {file = "contourpy-1.3.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9c0da700bf58f6e0b65312d0a5e695179a71d0163957fa381bb3c1f72972537c"},
+ {file = "contourpy-1.3.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eb8b141bb00fa977d9122636b16aa67d37fd40a3d8b52dd837e536d64b9a4d06"},
+ {file = "contourpy-1.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3634b5385c6716c258d0419c46d05c8aa7dc8cb70326c9a4fb66b69ad2b52e09"},
+ {file = "contourpy-1.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0dce35502151b6bd35027ac39ba6e5a44be13a68f55735c3612c568cac3805fd"},
+ {file = "contourpy-1.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:aea348f053c645100612b333adc5983d87be69acdc6d77d3169c090d3b01dc35"},
+ {file = "contourpy-1.3.0-cp312-cp312-win32.whl", hash = "sha256:90f73a5116ad1ba7174341ef3ea5c3150ddf20b024b98fb0c3b29034752c8aeb"},
+ {file = "contourpy-1.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:b11b39aea6be6764f84360fce6c82211a9db32a7c7de8fa6dd5397cf1d079c3b"},
+ {file = "contourpy-1.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3e1c7fa44aaae40a2247e2e8e0627f4bea3dd257014764aa644f319a5f8600e3"},
+ {file = "contourpy-1.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:364174c2a76057feef647c802652f00953b575723062560498dc7930fc9b1cb7"},
+ {file = "contourpy-1.3.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:32b238b3b3b649e09ce9aaf51f0c261d38644bdfa35cbaf7b263457850957a84"},
+ {file = "contourpy-1.3.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d51fca85f9f7ad0b65b4b9fe800406d0d77017d7270d31ec3fb1cc07358fdea0"},
+ {file = "contourpy-1.3.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:732896af21716b29ab3e988d4ce14bc5133733b85956316fb0c56355f398099b"},
+ {file = "contourpy-1.3.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d73f659398a0904e125280836ae6f88ba9b178b2fed6884f3b1f95b989d2c8da"},
+ {file = "contourpy-1.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c6c7c2408b7048082932cf4e641fa3b8ca848259212f51c8c59c45aa7ac18f14"},
+ {file = "contourpy-1.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f317576606de89da6b7e0861cf6061f6146ead3528acabff9236458a6ba467f8"},
+ {file = "contourpy-1.3.0-cp313-cp313-win32.whl", hash = "sha256:31cd3a85dbdf1fc002280c65caa7e2b5f65e4a973fcdf70dd2fdcb9868069294"},
+ {file = "contourpy-1.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:4553c421929ec95fb07b3aaca0fae668b2eb5a5203d1217ca7c34c063c53d087"},
+ {file = "contourpy-1.3.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:345af746d7766821d05d72cb8f3845dfd08dd137101a2cb9b24de277d716def8"},
+ {file = "contourpy-1.3.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3bb3808858a9dc68f6f03d319acd5f1b8a337e6cdda197f02f4b8ff67ad2057b"},
+ {file = "contourpy-1.3.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:420d39daa61aab1221567b42eecb01112908b2cab7f1b4106a52caaec8d36973"},
+ {file = "contourpy-1.3.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4d63ee447261e963af02642ffcb864e5a2ee4cbfd78080657a9880b8b1868e18"},
+ {file = "contourpy-1.3.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:167d6c890815e1dac9536dca00828b445d5d0df4d6a8c6adb4a7ec3166812fa8"},
+ {file = "contourpy-1.3.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:710a26b3dc80c0e4febf04555de66f5fd17e9cf7170a7b08000601a10570bda6"},
+ {file = "contourpy-1.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:75ee7cb1a14c617f34a51d11fa7524173e56551646828353c4af859c56b766e2"},
+ {file = "contourpy-1.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:33c92cdae89ec5135d036e7218e69b0bb2851206077251f04a6c4e0e21f03927"},
+ {file = "contourpy-1.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a11077e395f67ffc2c44ec2418cfebed032cd6da3022a94fc227b6faf8e2acb8"},
+ {file = "contourpy-1.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e8134301d7e204c88ed7ab50028ba06c683000040ede1d617298611f9dc6240c"},
+ {file = "contourpy-1.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e12968fdfd5bb45ffdf6192a590bd8ddd3ba9e58360b29683c6bb71a7b41edca"},
+ {file = "contourpy-1.3.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fd2a0fc506eccaaa7595b7e1418951f213cf8255be2600f1ea1b61e46a60c55f"},
+ {file = "contourpy-1.3.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4cfb5c62ce023dfc410d6059c936dcf96442ba40814aefbfa575425a3a7f19dc"},
+ {file = "contourpy-1.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68a32389b06b82c2fdd68276148d7b9275b5f5cf13e5417e4252f6d1a34f72a2"},
+ {file = "contourpy-1.3.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:94e848a6b83da10898cbf1311a815f770acc9b6a3f2d646f330d57eb4e87592e"},
+ {file = "contourpy-1.3.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:d78ab28a03c854a873787a0a42254a0ccb3cb133c672f645c9f9c8f3ae9d0800"},
+ {file = "contourpy-1.3.0-cp39-cp39-win32.whl", hash = "sha256:81cb5ed4952aae6014bc9d0421dec7c5835c9c8c31cdf51910b708f548cf58e5"},
+ {file = "contourpy-1.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:14e262f67bd7e6eb6880bc564dcda30b15e351a594657e55b7eec94b6ef72843"},
+ {file = "contourpy-1.3.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:fe41b41505a5a33aeaed2a613dccaeaa74e0e3ead6dd6fd3a118fb471644fd6c"},
+ {file = "contourpy-1.3.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eca7e17a65f72a5133bdbec9ecf22401c62bcf4821361ef7811faee695799779"},
+ {file = "contourpy-1.3.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:1ec4dc6bf570f5b22ed0d7efba0dfa9c5b9e0431aeea7581aa217542d9e809a4"},
+ {file = "contourpy-1.3.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:00ccd0dbaad6d804ab259820fa7cb0b8036bda0686ef844d24125d8287178ce0"},
+ {file = "contourpy-1.3.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ca947601224119117f7c19c9cdf6b3ab54c5726ef1d906aa4a69dfb6dd58102"},
+ {file = "contourpy-1.3.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:c6ec93afeb848a0845a18989da3beca3eec2c0f852322efe21af1931147d12cb"},
+ {file = "contourpy-1.3.0.tar.gz", hash = "sha256:7ffa0db17717a8ffb127efd0c95a4362d996b892c2904db72428d5b52e1938a4"},
+]
+
+[package.dependencies]
+numpy = ">=1.23"
+
+[package.extras]
+bokeh = ["bokeh", "selenium"]
+docs = ["furo", "sphinx (>=7.2)", "sphinx-copybutton"]
+mypy = ["contourpy[bokeh,docs]", "docutils-stubs", "mypy (==1.11.1)", "types-Pillow"]
+test = ["Pillow", "contourpy[test-no-images]", "matplotlib"]
+test-no-images = ["pytest", "pytest-cov", "pytest-rerunfailures", "pytest-xdist", "wurlitzer"]
+
+[[package]]
+name = "contourpy"
+version = "1.3.3"
+description = "Python library for calculating contours of 2D quadrilateral grids"
+optional = false
+python-versions = ">=3.11"
+groups = ["main"]
+markers = "python_version >= \"3.11\""
+files = [
+ {file = "contourpy-1.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:709a48ef9a690e1343202916450bc48b9e51c049b089c7f79a267b46cffcdaa1"},
+ {file = "contourpy-1.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:23416f38bfd74d5d28ab8429cc4d63fa67d5068bd711a85edb1c3fb0c3e2f381"},
+ {file = "contourpy-1.3.3-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:929ddf8c4c7f348e4c0a5a3a714b5c8542ffaa8c22954862a46ca1813b667ee7"},
+ {file = "contourpy-1.3.3-cp311-cp311-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9e999574eddae35f1312c2b4b717b7885d4edd6cb46700e04f7f02db454e67c1"},
+ {file = "contourpy-1.3.3-cp311-cp311-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0bf67e0e3f482cb69779dd3061b534eb35ac9b17f163d851e2a547d56dba0a3a"},
+ {file = "contourpy-1.3.3-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:51e79c1f7470158e838808d4a996fa9bac72c498e93d8ebe5119bc1e6becb0db"},
+ {file = "contourpy-1.3.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:598c3aaece21c503615fd59c92a3598b428b2f01bfb4b8ca9c4edeecc2438620"},
+ {file = "contourpy-1.3.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:322ab1c99b008dad206d406bb61d014cf0174df491ae9d9d0fac6a6fda4f977f"},
+ {file = "contourpy-1.3.3-cp311-cp311-win32.whl", hash = "sha256:fd907ae12cd483cd83e414b12941c632a969171bf90fc937d0c9f268a31cafff"},
+ {file = "contourpy-1.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:3519428f6be58431c56581f1694ba8e50626f2dd550af225f82fb5f5814d2a42"},
+ {file = "contourpy-1.3.3-cp311-cp311-win_arm64.whl", hash = "sha256:15ff10bfada4bf92ec8b31c62bf7c1834c244019b4a33095a68000d7075df470"},
+ {file = "contourpy-1.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b08a32ea2f8e42cf1d4be3169a98dd4be32bafe4f22b6c4cb4ba810fa9e5d2cb"},
+ {file = "contourpy-1.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:556dba8fb6f5d8742f2923fe9457dbdd51e1049c4a43fd3986a0b14a1d815fc6"},
+ {file = "contourpy-1.3.3-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92d9abc807cf7d0e047b95ca5d957cf4792fcd04e920ca70d48add15c1a90ea7"},
+ {file = "contourpy-1.3.3-cp312-cp312-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b2e8faa0ed68cb29af51edd8e24798bb661eac3bd9f65420c1887b6ca89987c8"},
+ {file = "contourpy-1.3.3-cp312-cp312-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:626d60935cf668e70a5ce6ff184fd713e9683fb458898e4249b63be9e28286ea"},
+ {file = "contourpy-1.3.3-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4d00e655fcef08aba35ec9610536bfe90267d7ab5ba944f7032549c55a146da1"},
+ {file = "contourpy-1.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:451e71b5a7d597379ef572de31eeb909a87246974d960049a9848c3bc6c41bf7"},
+ {file = "contourpy-1.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:459c1f020cd59fcfe6650180678a9993932d80d44ccde1fa1868977438f0b411"},
+ {file = "contourpy-1.3.3-cp312-cp312-win32.whl", hash = "sha256:023b44101dfe49d7d53932be418477dba359649246075c996866106da069af69"},
+ {file = "contourpy-1.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:8153b8bfc11e1e4d75bcb0bff1db232f9e10b274e0929de9d608027e0d34ff8b"},
+ {file = "contourpy-1.3.3-cp312-cp312-win_arm64.whl", hash = "sha256:07ce5ed73ecdc4a03ffe3e1b3e3c1166db35ae7584be76f65dbbe28a7791b0cc"},
+ {file = "contourpy-1.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:177fb367556747a686509d6fef71d221a4b198a3905fe824430e5ea0fda54eb5"},
+ {file = "contourpy-1.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d002b6f00d73d69333dac9d0b8d5e84d9724ff9ef044fd63c5986e62b7c9e1b1"},
+ {file = "contourpy-1.3.3-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:348ac1f5d4f1d66d3322420f01d42e43122f43616e0f194fc1c9f5d830c5b286"},
+ {file = "contourpy-1.3.3-cp313-cp313-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:655456777ff65c2c548b7c454af9c6f33f16c8884f11083244b5819cc214f1b5"},
+ {file = "contourpy-1.3.3-cp313-cp313-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:644a6853d15b2512d67881586bd03f462c7ab755db95f16f14d7e238f2852c67"},
+ {file = "contourpy-1.3.3-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4debd64f124ca62069f313a9cb86656ff087786016d76927ae2cf37846b006c9"},
+ {file = "contourpy-1.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a15459b0f4615b00bbd1e91f1b9e19b7e63aea7483d03d804186f278c0af2659"},
+ {file = "contourpy-1.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ca0fdcd73925568ca027e0b17ab07aad764be4706d0a925b89227e447d9737b7"},
+ {file = "contourpy-1.3.3-cp313-cp313-win32.whl", hash = "sha256:b20c7c9a3bf701366556e1b1984ed2d0cedf999903c51311417cf5f591d8c78d"},
+ {file = "contourpy-1.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:1cadd8b8969f060ba45ed7c1b714fe69185812ab43bd6b86a9123fe8f99c3263"},
+ {file = "contourpy-1.3.3-cp313-cp313-win_arm64.whl", hash = "sha256:fd914713266421b7536de2bfa8181aa8c699432b6763a0ea64195ebe28bff6a9"},
+ {file = "contourpy-1.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:88df9880d507169449d434c293467418b9f6cbe82edd19284aa0409e7fdb933d"},
+ {file = "contourpy-1.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:d06bb1f751ba5d417047db62bca3c8fde202b8c11fb50742ab3ab962c81e8216"},
+ {file = "contourpy-1.3.3-cp313-cp313t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e4e6b05a45525357e382909a4c1600444e2a45b4795163d3b22669285591c1ae"},
+ {file = "contourpy-1.3.3-cp313-cp313t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ab3074b48c4e2cf1a960e6bbeb7f04566bf36b1861d5c9d4d8ac04b82e38ba20"},
+ {file = "contourpy-1.3.3-cp313-cp313t-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c3d53c796f8647d6deb1abe867daeb66dcc8a97e8455efa729516b997b8ed99"},
+ {file = "contourpy-1.3.3-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50ed930df7289ff2a8d7afeb9603f8289e5704755c7e5c3bbd929c90c817164b"},
+ {file = "contourpy-1.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4feffb6537d64b84877da813a5c30f1422ea5739566abf0bd18065ac040e120a"},
+ {file = "contourpy-1.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2b7e9480ffe2b0cd2e787e4df64270e3a0440d9db8dc823312e2c940c167df7e"},
+ {file = "contourpy-1.3.3-cp313-cp313t-win32.whl", hash = "sha256:283edd842a01e3dcd435b1c5116798d661378d83d36d337b8dde1d16a5fc9ba3"},
+ {file = "contourpy-1.3.3-cp313-cp313t-win_amd64.whl", hash = "sha256:87acf5963fc2b34825e5b6b048f40e3635dd547f590b04d2ab317c2619ef7ae8"},
+ {file = "contourpy-1.3.3-cp313-cp313t-win_arm64.whl", hash = "sha256:3c30273eb2a55024ff31ba7d052dde990d7d8e5450f4bbb6e913558b3d6c2301"},
+ {file = "contourpy-1.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fde6c716d51c04b1c25d0b90364d0be954624a0ee9d60e23e850e8d48353d07a"},
+ {file = "contourpy-1.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:cbedb772ed74ff5be440fa8eee9bd49f64f6e3fc09436d9c7d8f1c287b121d77"},
+ {file = "contourpy-1.3.3-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:22e9b1bd7a9b1d652cd77388465dc358dafcd2e217d35552424aa4f996f524f5"},
+ {file = "contourpy-1.3.3-cp314-cp314-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a22738912262aa3e254e4f3cb079a95a67132fc5a063890e224393596902f5a4"},
+ {file = "contourpy-1.3.3-cp314-cp314-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:afe5a512f31ee6bd7d0dda52ec9864c984ca3d66664444f2d72e0dc4eb832e36"},
+ {file = "contourpy-1.3.3-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f64836de09927cba6f79dcd00fdd7d5329f3fccc633468507079c829ca4db4e3"},
+ {file = "contourpy-1.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:1fd43c3be4c8e5fd6e4f2baeae35ae18176cf2e5cced681cca908addf1cdd53b"},
+ {file = "contourpy-1.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6afc576f7b33cf00996e5c1102dc2a8f7cc89e39c0b55df93a0b78c1bd992b36"},
+ {file = "contourpy-1.3.3-cp314-cp314-win32.whl", hash = "sha256:66c8a43a4f7b8df8b71ee1840e4211a3c8d93b214b213f590e18a1beca458f7d"},
+ {file = "contourpy-1.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:cf9022ef053f2694e31d630feaacb21ea24224be1c3ad0520b13d844274614fd"},
+ {file = "contourpy-1.3.3-cp314-cp314-win_arm64.whl", hash = "sha256:95b181891b4c71de4bb404c6621e7e2390745f887f2a026b2d99e92c17892339"},
+ {file = "contourpy-1.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:33c82d0138c0a062380332c861387650c82e4cf1747aaa6938b9b6516762e772"},
+ {file = "contourpy-1.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ea37e7b45949df430fe649e5de8351c423430046a2af20b1c1961cae3afcda77"},
+ {file = "contourpy-1.3.3-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d304906ecc71672e9c89e87c4675dc5c2645e1f4269a5063b99b0bb29f232d13"},
+ {file = "contourpy-1.3.3-cp314-cp314t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ca658cd1a680a5c9ea96dc61cdbae1e85c8f25849843aa799dfd3cb370ad4fbe"},
+ {file = "contourpy-1.3.3-cp314-cp314t-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ab2fd90904c503739a75b7c8c5c01160130ba67944a7b77bbf36ef8054576e7f"},
+ {file = "contourpy-1.3.3-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7301b89040075c30e5768810bc96a8e8d78085b47d8be6e4c3f5a0b4ed478a0"},
+ {file = "contourpy-1.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:2a2a8b627d5cc6b7c41a4beff6c5ad5eb848c88255fda4a8745f7e901b32d8e4"},
+ {file = "contourpy-1.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:fd6ec6be509c787f1caf6b247f0b1ca598bef13f4ddeaa126b7658215529ba0f"},
+ {file = "contourpy-1.3.3-cp314-cp314t-win32.whl", hash = "sha256:e74a9a0f5e3fff48fb5a7f2fd2b9b70a3fe014a67522f79b7cca4c0c7e43c9ae"},
+ {file = "contourpy-1.3.3-cp314-cp314t-win_amd64.whl", hash = "sha256:13b68d6a62db8eafaebb8039218921399baf6e47bf85006fd8529f2a08ef33fc"},
+ {file = "contourpy-1.3.3-cp314-cp314t-win_arm64.whl", hash = "sha256:b7448cb5a725bb1e35ce88771b86fba35ef418952474492cf7c764059933ff8b"},
+ {file = "contourpy-1.3.3-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:cd5dfcaeb10f7b7f9dc8941717c6c2ade08f587be2226222c12b25f0483ed497"},
+ {file = "contourpy-1.3.3-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:0c1fc238306b35f246d61a1d416a627348b5cf0648648a031e14bb8705fcdfe8"},
+ {file = "contourpy-1.3.3-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:70f9aad7de812d6541d29d2bbf8feb22ff7e1c299523db288004e3157ff4674e"},
+ {file = "contourpy-1.3.3-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5ed3657edf08512fc3fe81b510e35c2012fbd3081d2e26160f27ca28affec989"},
+ {file = "contourpy-1.3.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:3d1a3799d62d45c18bafd41c5fa05120b96a28079f2393af559b843d1a966a77"},
+ {file = "contourpy-1.3.3.tar.gz", hash = "sha256:083e12155b210502d0bca491432bb04d56dc3432f95a979b429f2848c3dbe880"},
+]
+
+[package.dependencies]
+numpy = ">=1.25"
+
+[package.extras]
+bokeh = ["bokeh", "selenium"]
+docs = ["furo", "sphinx (>=7.2)", "sphinx-copybutton"]
+mypy = ["bokeh", "contourpy[bokeh,docs]", "docutils-stubs", "mypy (==1.17.0)", "types-Pillow"]
+test = ["Pillow", "contourpy[test-no-images]", "matplotlib"]
+test-no-images = ["pytest", "pytest-cov", "pytest-rerunfailures", "pytest-xdist", "wurlitzer"]
+
+[[package]]
+name = "coverage"
+version = "7.10.6"
+description = "Code coverage measurement for Python"
+optional = false
+python-versions = ">=3.9"
+groups = ["dev"]
+files = [
+ {file = "coverage-7.10.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:70e7bfbd57126b5554aa482691145f798d7df77489a177a6bef80de78860a356"},
+ {file = "coverage-7.10.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e41be6f0f19da64af13403e52f2dec38bbc2937af54df8ecef10850ff8d35301"},
+ {file = "coverage-7.10.6-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:c61fc91ab80b23f5fddbee342d19662f3d3328173229caded831aa0bd7595460"},
+ {file = "coverage-7.10.6-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:10356fdd33a7cc06e8051413140bbdc6f972137508a3572e3f59f805cd2832fd"},
+ {file = "coverage-7.10.6-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:80b1695cf7c5ebe7b44bf2521221b9bb8cdf69b1f24231149a7e3eb1ae5fa2fb"},
+ {file = "coverage-7.10.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:2e4c33e6378b9d52d3454bd08847a8651f4ed23ddbb4a0520227bd346382bbc6"},
+ {file = "coverage-7.10.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:c8a3ec16e34ef980a46f60dc6ad86ec60f763c3f2fa0db6d261e6e754f72e945"},
+ {file = "coverage-7.10.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7d79dabc0a56f5af990cc6da9ad1e40766e82773c075f09cc571e2076fef882e"},
+ {file = "coverage-7.10.6-cp310-cp310-win32.whl", hash = "sha256:86b9b59f2b16e981906e9d6383eb6446d5b46c278460ae2c36487667717eccf1"},
+ {file = "coverage-7.10.6-cp310-cp310-win_amd64.whl", hash = "sha256:e132b9152749bd33534e5bd8565c7576f135f157b4029b975e15ee184325f528"},
+ {file = "coverage-7.10.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c706db3cabb7ceef779de68270150665e710b46d56372455cd741184f3868d8f"},
+ {file = "coverage-7.10.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8e0c38dc289e0508ef68ec95834cb5d2e96fdbe792eaccaa1bccac3966bbadcc"},
+ {file = "coverage-7.10.6-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:752a3005a1ded28f2f3a6e8787e24f28d6abe176ca64677bcd8d53d6fe2ec08a"},
+ {file = "coverage-7.10.6-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:689920ecfd60f992cafca4f5477d55720466ad2c7fa29bb56ac8d44a1ac2b47a"},
+ {file = "coverage-7.10.6-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ec98435796d2624d6905820a42f82149ee9fc4f2d45c2c5bc5a44481cc50db62"},
+ {file = "coverage-7.10.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b37201ce4a458c7a758ecc4efa92fa8ed783c66e0fa3c42ae19fc454a0792153"},
+ {file = "coverage-7.10.6-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:2904271c80898663c810a6b067920a61dd8d38341244a3605bd31ab55250dad5"},
+ {file = "coverage-7.10.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5aea98383463d6e1fa4e95416d8de66f2d0cb588774ee20ae1b28df826bcb619"},
+ {file = "coverage-7.10.6-cp311-cp311-win32.whl", hash = "sha256:e3fb1fa01d3598002777dd259c0c2e6d9d5e10e7222976fc8e03992f972a2cba"},
+ {file = "coverage-7.10.6-cp311-cp311-win_amd64.whl", hash = "sha256:f35ed9d945bece26553d5b4c8630453169672bea0050a564456eb88bdffd927e"},
+ {file = "coverage-7.10.6-cp311-cp311-win_arm64.whl", hash = "sha256:99e1a305c7765631d74b98bf7dbf54eeea931f975e80f115437d23848ee8c27c"},
+ {file = "coverage-7.10.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5b2dd6059938063a2c9fee1af729d4f2af28fd1a545e9b7652861f0d752ebcea"},
+ {file = "coverage-7.10.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:388d80e56191bf846c485c14ae2bc8898aa3124d9d35903fef7d907780477634"},
+ {file = "coverage-7.10.6-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:90cb5b1a4670662719591aa92d0095bb41714970c0b065b02a2610172dbf0af6"},
+ {file = "coverage-7.10.6-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:961834e2f2b863a0e14260a9a273aff07ff7818ab6e66d2addf5628590c628f9"},
+ {file = "coverage-7.10.6-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bf9a19f5012dab774628491659646335b1928cfc931bf8d97b0d5918dd58033c"},
+ {file = "coverage-7.10.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:99c4283e2a0e147b9c9cc6bc9c96124de9419d6044837e9799763a0e29a7321a"},
+ {file = "coverage-7.10.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:282b1b20f45df57cc508c1e033403f02283adfb67d4c9c35a90281d81e5c52c5"},
+ {file = "coverage-7.10.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8cdbe264f11afd69841bd8c0d83ca10b5b32853263ee62e6ac6a0ab63895f972"},
+ {file = "coverage-7.10.6-cp312-cp312-win32.whl", hash = "sha256:a517feaf3a0a3eca1ee985d8373135cfdedfbba3882a5eab4362bda7c7cf518d"},
+ {file = "coverage-7.10.6-cp312-cp312-win_amd64.whl", hash = "sha256:856986eadf41f52b214176d894a7de05331117f6035a28ac0016c0f63d887629"},
+ {file = "coverage-7.10.6-cp312-cp312-win_arm64.whl", hash = "sha256:acf36b8268785aad739443fa2780c16260ee3fa09d12b3a70f772ef100939d80"},
+ {file = "coverage-7.10.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ffea0575345e9ee0144dfe5701aa17f3ba546f8c3bb48db62ae101afb740e7d6"},
+ {file = "coverage-7.10.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:95d91d7317cde40a1c249d6b7382750b7e6d86fad9d8eaf4fa3f8f44cf171e80"},
+ {file = "coverage-7.10.6-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3e23dd5408fe71a356b41baa82892772a4cefcf758f2ca3383d2aa39e1b7a003"},
+ {file = "coverage-7.10.6-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0f3f56e4cb573755e96a16501a98bf211f100463d70275759e73f3cbc00d4f27"},
+ {file = "coverage-7.10.6-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:db4a1d897bbbe7339946ffa2fe60c10cc81c43fab8b062d3fcb84188688174a4"},
+ {file = "coverage-7.10.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d8fd7879082953c156d5b13c74aa6cca37f6a6f4747b39538504c3f9c63d043d"},
+ {file = "coverage-7.10.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:28395ca3f71cd103b8c116333fa9db867f3a3e1ad6a084aa3725ae002b6583bc"},
+ {file = "coverage-7.10.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:61c950fc33d29c91b9e18540e1aed7d9f6787cc870a3e4032493bbbe641d12fc"},
+ {file = "coverage-7.10.6-cp313-cp313-win32.whl", hash = "sha256:160c00a5e6b6bdf4e5984b0ef21fc860bc94416c41b7df4d63f536d17c38902e"},
+ {file = "coverage-7.10.6-cp313-cp313-win_amd64.whl", hash = "sha256:628055297f3e2aa181464c3808402887643405573eb3d9de060d81531fa79d32"},
+ {file = "coverage-7.10.6-cp313-cp313-win_arm64.whl", hash = "sha256:df4ec1f8540b0bcbe26ca7dd0f541847cc8a108b35596f9f91f59f0c060bfdd2"},
+ {file = "coverage-7.10.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:c9a8b7a34a4de3ed987f636f71881cd3b8339f61118b1aa311fbda12741bff0b"},
+ {file = "coverage-7.10.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:8dd5af36092430c2b075cee966719898f2ae87b636cefb85a653f1d0ba5d5393"},
+ {file = "coverage-7.10.6-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:b0353b0f0850d49ada66fdd7d0c7cdb0f86b900bb9e367024fd14a60cecc1e27"},
+ {file = "coverage-7.10.6-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d6b9ae13d5d3e8aeca9ca94198aa7b3ebbc5acfada557d724f2a1f03d2c0b0df"},
+ {file = "coverage-7.10.6-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:675824a363cc05781b1527b39dc2587b8984965834a748177ee3c37b64ffeafb"},
+ {file = "coverage-7.10.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:692d70ea725f471a547c305f0d0fc6a73480c62fb0da726370c088ab21aed282"},
+ {file = "coverage-7.10.6-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:851430a9a361c7a8484a36126d1d0ff8d529d97385eacc8dfdc9bfc8c2d2cbe4"},
+ {file = "coverage-7.10.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d9369a23186d189b2fc95cc08b8160ba242057e887d766864f7adf3c46b2df21"},
+ {file = "coverage-7.10.6-cp313-cp313t-win32.whl", hash = "sha256:92be86fcb125e9bda0da7806afd29a3fd33fdf58fba5d60318399adf40bf37d0"},
+ {file = "coverage-7.10.6-cp313-cp313t-win_amd64.whl", hash = "sha256:6b3039e2ca459a70c79523d39347d83b73f2f06af5624905eba7ec34d64d80b5"},
+ {file = "coverage-7.10.6-cp313-cp313t-win_arm64.whl", hash = "sha256:3fb99d0786fe17b228eab663d16bee2288e8724d26a199c29325aac4b0319b9b"},
+ {file = "coverage-7.10.6-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:6008a021907be8c4c02f37cdc3ffb258493bdebfeaf9a839f9e71dfdc47b018e"},
+ {file = "coverage-7.10.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:5e75e37f23eb144e78940b40395b42f2321951206a4f50e23cfd6e8a198d3ceb"},
+ {file = "coverage-7.10.6-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0f7cb359a448e043c576f0da00aa8bfd796a01b06aa610ca453d4dde09cc1034"},
+ {file = "coverage-7.10.6-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c68018e4fc4e14b5668f1353b41ccf4bc83ba355f0e1b3836861c6f042d89ac1"},
+ {file = "coverage-7.10.6-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cd4b2b0707fc55afa160cd5fc33b27ccbf75ca11d81f4ec9863d5793fc6df56a"},
+ {file = "coverage-7.10.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4cec13817a651f8804a86e4f79d815b3b28472c910e099e4d5a0e8a3b6a1d4cb"},
+ {file = "coverage-7.10.6-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:f2a6a8e06bbda06f78739f40bfb56c45d14eb8249d0f0ea6d4b3d48e1f7c695d"},
+ {file = "coverage-7.10.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:081b98395ced0d9bcf60ada7661a0b75f36b78b9d7e39ea0790bb4ed8da14747"},
+ {file = "coverage-7.10.6-cp314-cp314-win32.whl", hash = "sha256:6937347c5d7d069ee776b2bf4e1212f912a9f1f141a429c475e6089462fcecc5"},
+ {file = "coverage-7.10.6-cp314-cp314-win_amd64.whl", hash = "sha256:adec1d980fa07e60b6ef865f9e5410ba760e4e1d26f60f7e5772c73b9a5b0713"},
+ {file = "coverage-7.10.6-cp314-cp314-win_arm64.whl", hash = "sha256:a80f7aef9535442bdcf562e5a0d5a5538ce8abe6bb209cfbf170c462ac2c2a32"},
+ {file = "coverage-7.10.6-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:0de434f4fbbe5af4fa7989521c655c8c779afb61c53ab561b64dcee6149e4c65"},
+ {file = "coverage-7.10.6-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6e31b8155150c57e5ac43ccd289d079eb3f825187d7c66e755a055d2c85794c6"},
+ {file = "coverage-7.10.6-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:98cede73eb83c31e2118ae8d379c12e3e42736903a8afcca92a7218e1f2903b0"},
+ {file = "coverage-7.10.6-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f863c08f4ff6b64fa8045b1e3da480f5374779ef187f07b82e0538c68cb4ff8e"},
+ {file = "coverage-7.10.6-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2b38261034fda87be356f2c3f42221fdb4171c3ce7658066ae449241485390d5"},
+ {file = "coverage-7.10.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:0e93b1476b79eae849dc3872faeb0bf7948fd9ea34869590bc16a2a00b9c82a7"},
+ {file = "coverage-7.10.6-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:ff8a991f70f4c0cf53088abf1e3886edcc87d53004c7bb94e78650b4d3dac3b5"},
+ {file = "coverage-7.10.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ac765b026c9f33044419cbba1da913cfb82cca1b60598ac1c7a5ed6aac4621a0"},
+ {file = "coverage-7.10.6-cp314-cp314t-win32.whl", hash = "sha256:441c357d55f4936875636ef2cfb3bee36e466dcf50df9afbd398ce79dba1ebb7"},
+ {file = "coverage-7.10.6-cp314-cp314t-win_amd64.whl", hash = "sha256:073711de3181b2e204e4870ac83a7c4853115b42e9cd4d145f2231e12d670930"},
+ {file = "coverage-7.10.6-cp314-cp314t-win_arm64.whl", hash = "sha256:137921f2bac5559334ba66122b753db6dc5d1cf01eb7b64eb412bb0d064ef35b"},
+ {file = "coverage-7.10.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:90558c35af64971d65fbd935c32010f9a2f52776103a259f1dee865fe8259352"},
+ {file = "coverage-7.10.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8953746d371e5695405806c46d705a3cd170b9cc2b9f93953ad838f6c1e58612"},
+ {file = "coverage-7.10.6-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:c83f6afb480eae0313114297d29d7c295670a41c11b274e6bca0c64540c1ce7b"},
+ {file = "coverage-7.10.6-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7eb68d356ba0cc158ca535ce1381dbf2037fa8cb5b1ae5ddfc302e7317d04144"},
+ {file = "coverage-7.10.6-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5b15a87265e96307482746d86995f4bff282f14b027db75469c446da6127433b"},
+ {file = "coverage-7.10.6-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fc53ba868875bfbb66ee447d64d6413c2db91fddcfca57025a0e7ab5b07d5862"},
+ {file = "coverage-7.10.6-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:efeda443000aa23f276f4df973cb82beca682fd800bb119d19e80504ffe53ec2"},
+ {file = "coverage-7.10.6-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:9702b59d582ff1e184945d8b501ffdd08d2cee38d93a2206aa5f1365ce0b8d78"},
+ {file = "coverage-7.10.6-cp39-cp39-win32.whl", hash = "sha256:2195f8e16ba1a44651ca684db2ea2b2d4b5345da12f07d9c22a395202a05b23c"},
+ {file = "coverage-7.10.6-cp39-cp39-win_amd64.whl", hash = "sha256:f32ff80e7ef6a5b5b606ea69a36e97b219cd9dc799bcf2963018a4d8f788cfbf"},
+ {file = "coverage-7.10.6-py3-none-any.whl", hash = "sha256:92c4ecf6bf11b2e85fd4d8204814dc26e6a19f0c9d938c207c5cb0eadfcabbe3"},
+ {file = "coverage-7.10.6.tar.gz", hash = "sha256:f644a3ae5933a552a29dbb9aa2f90c677a875f80ebea028e5a52a4f429044b90"},
+]
+
+[package.dependencies]
+tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""}
+
+[package.extras]
+toml = ["tomli ; python_full_version <= \"3.11.0a6\""]
+
+[[package]]
+name = "cycler"
+version = "0.12.1"
+description = "Composable style cycles"
+optional = false
+python-versions = ">=3.8"
+groups = ["main"]
+files = [
+ {file = "cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30"},
+ {file = "cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c"},
+]
+
+[package.extras]
+docs = ["ipython", "matplotlib", "numpydoc", "sphinx"]
+tests = ["pytest", "pytest-cov", "pytest-xdist"]
+
+[[package]]
+name = "datasets"
+version = "4.0.0"
+description = "HuggingFace community-driven open-source library of datasets"
+optional = false
+python-versions = ">=3.9.0"
+groups = ["main"]
+files = [
+ {file = "datasets-4.0.0-py3-none-any.whl", hash = "sha256:7ef95e62025fd122882dbce6cb904c8cd3fbc829de6669a5eb939c77d50e203d"},
+ {file = "datasets-4.0.0.tar.gz", hash = "sha256:9657e7140a9050db13443ba21cb5de185af8af944479b00e7ff1e00a61c8dbf1"},
+]
+
+[package.dependencies]
+dill = ">=0.3.0,<0.3.9"
+filelock = "*"
+fsspec = {version = ">=2023.1.0,<=2025.3.0", extras = ["http"]}
+huggingface-hub = ">=0.24.0"
+multiprocess = "<0.70.17"
+numpy = ">=1.17"
+packaging = "*"
+pandas = "*"
+pyarrow = ">=15.0.0"
+pyyaml = ">=5.1"
+requests = ">=2.32.2"
+tqdm = ">=4.66.3"
+xxhash = "*"
+
+[package.extras]
+audio = ["soundfile (>=0.12.1)", "torch (>=2.7.0)", "torchcodec (>=0.4.0)"]
+benchmarks = ["tensorflow (==2.12.0)", "torch (==2.0.1)", "transformers (==4.30.1)"]
+dev = ["Pillow (>=9.4.0)", "absl-py", "aiohttp", "decorator", "elasticsearch (>=7.17.12,<8.0.0)", "faiss-cpu (>=1.8.0.post1)", "jax (>=0.3.14) ; sys_platform != \"win32\"", "jaxlib (>=0.3.14) ; sys_platform != \"win32\"", "joblib (<1.3.0)", "joblibspark", "lz4", "moto[server]", "numba (>=0.56.4)", "polars[timezone] (>=0.20.0)", "protobuf (<4.0.0)", "py7zr", "pyspark (>=3.4)", "pytest", "pytest-datadir", "pytest-xdist", "rarfile (>=4.0)", "ruff (>=0.3.0)", "soundfile (>=0.12.1)", "soundfile (>=0.12.1)", "sqlalchemy", "tensorflow (>=2.16.0) ; python_version >= \"3.10\" and sys_platform != \"win32\"", "tensorflow (>=2.6.0)", "tensorflow (>=2.6.0) ; python_version < \"3.10\" and sys_platform != \"win32\"", "tiktoken", "torch", "torch (>=2.0.0)", "torchcodec (>=0.4.0) ; sys_platform != \"win32\"", "torchdata", "transformers", "transformers (>=4.42.0)", "zstandard"]
+docs = ["tensorflow (>=2.6.0)", "torch", "transformers"]
+jax = ["jax (>=0.3.14)", "jaxlib (>=0.3.14)"]
+pdfs = ["pdfplumber (>=0.11.4)"]
+quality = ["ruff (>=0.3.0)"]
+tensorflow = ["tensorflow (>=2.6.0)"]
+tensorflow-gpu = ["tensorflow (>=2.6.0)"]
+tests = ["Pillow (>=9.4.0)", "absl-py", "aiohttp", "decorator", "elasticsearch (>=7.17.12,<8.0.0)", "faiss-cpu (>=1.8.0.post1)", "jax (>=0.3.14) ; sys_platform != \"win32\"", "jaxlib (>=0.3.14) ; sys_platform != \"win32\"", "joblib (<1.3.0)", "joblibspark", "lz4", "moto[server]", "numba (>=0.56.4)", "polars[timezone] (>=0.20.0)", "protobuf (<4.0.0)", "py7zr", "pyspark (>=3.4)", "pytest", "pytest-datadir", "pytest-xdist", "rarfile (>=4.0)", "soundfile (>=0.12.1)", "soundfile (>=0.12.1)", "sqlalchemy", "tensorflow (>=2.16.0) ; python_version >= \"3.10\" and sys_platform != \"win32\"", "tensorflow (>=2.6.0) ; python_version < \"3.10\" and sys_platform != \"win32\"", "tiktoken", "torch (>=2.0.0)", "torchcodec (>=0.4.0) ; sys_platform != \"win32\"", "torchdata", "transformers (>=4.42.0)", "zstandard"]
+tests-numpy2 = ["Pillow (>=9.4.0)", "absl-py", "aiohttp", "decorator", "elasticsearch (>=7.17.12,<8.0.0)", "jax (>=0.3.14) ; sys_platform != \"win32\"", "jaxlib (>=0.3.14) ; sys_platform != \"win32\"", "joblib (<1.3.0)", "joblibspark", "lz4", "moto[server]", "numba (>=0.56.4)", "polars[timezone] (>=0.20.0)", "protobuf (<4.0.0)", "py7zr", "pyspark (>=3.4)", "pytest", "pytest-datadir", "pytest-xdist", "rarfile (>=4.0)", "soundfile (>=0.12.1)", "soundfile (>=0.12.1)", "sqlalchemy", "tiktoken", "torch (>=2.0.0)", "torchcodec (>=0.4.0) ; sys_platform != \"win32\"", "torchdata", "transformers (>=4.42.0)", "zstandard"]
+torch = ["torch"]
+vision = ["Pillow (>=9.4.0)"]
+
+[[package]]
+name = "dill"
+version = "0.3.8"
+description = "serialize all of Python"
+optional = false
+python-versions = ">=3.8"
+groups = ["main", "dev"]
+files = [
+ {file = "dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7"},
+ {file = "dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca"},
+]
+
+[package.extras]
+graph = ["objgraph (>=1.7.2)"]
+profile = ["gprof2dot (>=2022.7.29)"]
+
+[[package]]
+name = "distlib"
+version = "0.4.0"
+description = "Distribution utilities"
+optional = false
+python-versions = "*"
+groups = ["dev"]
+files = [
+ {file = "distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16"},
+ {file = "distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d"},
+]
+
+[[package]]
+name = "docformatter"
+version = "1.7.7"
+description = "Formats docstrings to follow PEP 257"
+optional = false
+python-versions = "<4.0,>=3.9"
+groups = ["dev"]
+files = [
+ {file = "docformatter-1.7.7-py3-none-any.whl", hash = "sha256:7af49f8a46346a77858f6651f431b882c503c2f4442c8b4524b920c863277834"},
+ {file = "docformatter-1.7.7.tar.gz", hash = "sha256:ea0e1e8867e5af468dfc3f9e947b92230a55be9ec17cd1609556387bffac7978"},
+]
+
+[package.dependencies]
+charset_normalizer = ">=3.0.0,<4.0.0"
+untokenize = ">=0.1.1,<0.2.0"
+
+[package.extras]
+tomli = ["tomli (>=2.0.0,<3.0.0) ; python_version < \"3.11\""]
+
+[[package]]
+name = "docutils"
+version = "0.21.2"
+description = "Docutils -- Python Documentation Utilities"
+optional = false
+python-versions = ">=3.9"
+groups = ["docs"]
+files = [
+ {file = "docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2"},
+ {file = "docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f"},
+]
+
+[[package]]
+name = "eval-type-backport"
+version = "0.2.2"
+description = "Like `typing._eval_type`, but lets older Python versions use newer typing features."
+optional = false
+python-versions = ">=3.8"
+groups = ["benchmarks"]
+markers = "python_version == \"3.9\""
+files = [
+ {file = "eval_type_backport-0.2.2-py3-none-any.whl", hash = "sha256:cb6ad7c393517f476f96d456d0412ea80f0a8cf96f6892834cd9340149111b0a"},
+ {file = "eval_type_backport-0.2.2.tar.gz", hash = "sha256:f0576b4cf01ebb5bd358d02314d31846af5e07678387486e2c798af0e7d849c1"},
+]
+
+[package.extras]
+tests = ["pytest"]
+
+[[package]]
+name = "exceptiongroup"
+version = "1.3.0"
+description = "Backport of PEP 654 (exception groups)"
+optional = false
+python-versions = ">=3.7"
+groups = ["dev"]
+markers = "python_version < \"3.11\""
+files = [
+ {file = "exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10"},
+ {file = "exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88"},
+]
+
+[package.dependencies]
+typing-extensions = {version = ">=4.6.0", markers = "python_version < \"3.13\""}
+
+[package.extras]
+test = ["pytest (>=6)"]
+
+[[package]]
+name = "execnet"
+version = "2.1.1"
+description = "execnet: rapid multi-Python deployment"
+optional = false
+python-versions = ">=3.8"
+groups = ["dev"]
+files = [
+ {file = "execnet-2.1.1-py3-none-any.whl", hash = "sha256:26dee51f1b80cebd6d0ca8e74dd8745419761d3bef34163928cbebbdc4749fdc"},
+ {file = "execnet-2.1.1.tar.gz", hash = "sha256:5189b52c6121c24feae288166ab41b32549c7e2348652736540b9e6e7d4e72e3"},
+]
+
+[package.extras]
+testing = ["hatch", "pre-commit", "pytest", "tox"]
+
+[[package]]
+name = "filelock"
+version = "3.19.1"
+description = "A platform independent file lock."
+optional = false
+python-versions = ">=3.9"
+groups = ["main", "dev"]
+files = [
+ {file = "filelock-3.19.1-py3-none-any.whl", hash = "sha256:d38e30481def20772f5baf097c122c3babc4fcdb7e14e57049eb9d88c6dc017d"},
+ {file = "filelock-3.19.1.tar.gz", hash = "sha256:66eda1888b0171c998b35be2bcc0f6d75c388a7ce20c3f3f37aa8e96c2dddf58"},
+]
+
+[[package]]
+name = "flake8"
+version = "5.0.4"
+description = "the modular source code checker: pep8 pyflakes and co"
+optional = false
+python-versions = ">=3.6.1"
+groups = ["dev"]
+files = [
+ {file = "flake8-5.0.4-py2.py3-none-any.whl", hash = "sha256:7a1cf6b73744f5806ab95e526f6f0d8c01c66d7bbe349562d22dfca20610b248"},
+ {file = "flake8-5.0.4.tar.gz", hash = "sha256:6fbe320aad8d6b95cec8b8e47bc933004678dc63095be98528b7bdd2a9f510db"},
+]
+
+[package.dependencies]
+mccabe = ">=0.7.0,<0.8.0"
+pycodestyle = ">=2.9.0,<2.10.0"
+pyflakes = ">=2.5.0,<2.6.0"
+
+[[package]]
+name = "fonttools"
+version = "4.59.2"
+description = "Tools to manipulate font files"
+optional = false
+python-versions = ">=3.9"
+groups = ["main"]
+files = [
+ {file = "fonttools-4.59.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2a159e36ae530650acd13604f364b3a2477eff7408dcac6a640d74a3744d2514"},
+ {file = "fonttools-4.59.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8bd733e47bf4c6dee2b2d8af7a1f7b0c091909b22dbb969a29b2b991e61e5ba4"},
+ {file = "fonttools-4.59.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7bb32e0e33795e3b7795bb9b88cb6a9d980d3cbe26dd57642471be547708e17a"},
+ {file = "fonttools-4.59.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cdcdf7aad4bab7fd0f2938624a5a84eb4893be269f43a6701b0720b726f24df0"},
+ {file = "fonttools-4.59.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4d974312a9f405628e64f475b1f5015a61fd338f0a1b61d15c4822f97d6b045b"},
+ {file = "fonttools-4.59.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:12dc4670e6e6cc4553e8de190f86a549e08ca83a036363115d94a2d67488831e"},
+ {file = "fonttools-4.59.2-cp310-cp310-win32.whl", hash = "sha256:1603b85d5922042563eea518e272b037baf273b9a57d0f190852b0b075079000"},
+ {file = "fonttools-4.59.2-cp310-cp310-win_amd64.whl", hash = "sha256:2543b81641ea5b8ddfcae7926e62aafd5abc604320b1b119e5218c014a7a5d3c"},
+ {file = "fonttools-4.59.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:511946e8d7ea5c0d6c7a53c4cb3ee48eda9ab9797cd9bf5d95829a398400354f"},
+ {file = "fonttools-4.59.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8e5e2682cf7be766d84f462ba8828d01e00c8751a8e8e7ce12d7784ccb69a30d"},
+ {file = "fonttools-4.59.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5729e12a982dba3eeae650de48b06f3b9ddb51e9aee2fcaf195b7d09a96250e2"},
+ {file = "fonttools-4.59.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c52694eae5d652361d59ecdb5a2246bff7cff13b6367a12da8499e9df56d148d"},
+ {file = "fonttools-4.59.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f1f1bbc23ba1312bd8959896f46f667753b90216852d2a8cfa2d07e0cb234144"},
+ {file = "fonttools-4.59.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1a1bfe5378962825dabe741720885e8b9ae9745ec7ecc4a5ec1f1ce59a6062bf"},
+ {file = "fonttools-4.59.2-cp311-cp311-win32.whl", hash = "sha256:e937790f3c2c18a1cbc7da101550a84319eb48023a715914477d2e7faeaba570"},
+ {file = "fonttools-4.59.2-cp311-cp311-win_amd64.whl", hash = "sha256:9836394e2f4ce5f9c0a7690ee93bd90aa1adc6b054f1a57b562c5d242c903104"},
+ {file = "fonttools-4.59.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:82906d002c349cad647a7634b004825a7335f8159d0d035ae89253b4abf6f3ea"},
+ {file = "fonttools-4.59.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a10c1bd7644dc58f8862d8ba0cf9fb7fef0af01ea184ba6ce3f50ab7dfe74d5a"},
+ {file = "fonttools-4.59.2-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:738f31f23e0339785fd67652a94bc69ea49e413dfdb14dcb8c8ff383d249464e"},
+ {file = "fonttools-4.59.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ec99f9bdfee9cdb4a9172f9e8fd578cce5feb231f598909e0aecf5418da4f25"},
+ {file = "fonttools-4.59.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0476ea74161322e08c7a982f83558a2b81b491509984523a1a540baf8611cc31"},
+ {file = "fonttools-4.59.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:95922a922daa1f77cc72611747c156cfb38030ead72436a2c551d30ecef519b9"},
+ {file = "fonttools-4.59.2-cp312-cp312-win32.whl", hash = "sha256:39ad9612c6a622726a6a130e8ab15794558591f999673f1ee7d2f3d30f6a3e1c"},
+ {file = "fonttools-4.59.2-cp312-cp312-win_amd64.whl", hash = "sha256:980fd7388e461b19a881d35013fec32c713ffea1fc37aef2f77d11f332dfd7da"},
+ {file = "fonttools-4.59.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:381bde13216ba09489864467f6bc0c57997bd729abfbb1ce6f807ba42c06cceb"},
+ {file = "fonttools-4.59.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f33839aa091f7eef4e9078f5b7ab1b8ea4b1d8a50aeaef9fdb3611bba80869ec"},
+ {file = "fonttools-4.59.2-cp313-cp313-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6235fc06bcbdb40186f483ba9d5d68f888ea68aa3c8dac347e05a7c54346fbc8"},
+ {file = "fonttools-4.59.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:83ad6e5d06ef3a2884c4fa6384a20d6367b5cfe560e3b53b07c9dc65a7020e73"},
+ {file = "fonttools-4.59.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d029804c70fddf90be46ed5305c136cae15800a2300cb0f6bba96d48e770dde0"},
+ {file = "fonttools-4.59.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:95807a3b5e78f2714acaa26a33bc2143005cc05c0217b322361a772e59f32b89"},
+ {file = "fonttools-4.59.2-cp313-cp313-win32.whl", hash = "sha256:b3ebda00c3bb8f32a740b72ec38537d54c7c09f383a4cfefb0b315860f825b08"},
+ {file = "fonttools-4.59.2-cp313-cp313-win_amd64.whl", hash = "sha256:a72155928d7053bbde499d32a9c77d3f0f3d29ae72b5a121752481bcbd71e50f"},
+ {file = "fonttools-4.59.2-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:d09e487d6bfbe21195801323ba95c91cb3523f0fcc34016454d4d9ae9eaa57fe"},
+ {file = "fonttools-4.59.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:dec2f22486d7781087b173799567cffdcc75e9fb2f1c045f05f8317ccce76a3e"},
+ {file = "fonttools-4.59.2-cp314-cp314-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:1647201af10993090120da2e66e9526c4e20e88859f3e34aa05b8c24ded2a564"},
+ {file = "fonttools-4.59.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:47742c33fe65f41eabed36eec2d7313a8082704b7b808752406452f766c573fc"},
+ {file = "fonttools-4.59.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:92ac2d45794f95d1ad4cb43fa07e7e3776d86c83dc4b9918cf82831518165b4b"},
+ {file = "fonttools-4.59.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:fa9ecaf2dcef8941fb5719e16322345d730f4c40599bbf47c9753de40eb03882"},
+ {file = "fonttools-4.59.2-cp314-cp314-win32.whl", hash = "sha256:a8d40594982ed858780e18a7e4c80415af65af0f22efa7de26bdd30bf24e1e14"},
+ {file = "fonttools-4.59.2-cp314-cp314-win_amd64.whl", hash = "sha256:9cde8b6a6b05f68516573523f2013a3574cb2c75299d7d500f44de82ba947b80"},
+ {file = "fonttools-4.59.2-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:036cd87a2dbd7ef72f7b68df8314ced00b8d9973aee296f2464d06a836aeb9a9"},
+ {file = "fonttools-4.59.2-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:14870930181493b1d740b6f25483e20185e5aea58aec7d266d16da7be822b4bb"},
+ {file = "fonttools-4.59.2-cp314-cp314t-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7ff58ea1eb8fc7e05e9a949419f031890023f8785c925b44d6da17a6a7d6e85d"},
+ {file = "fonttools-4.59.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6dee142b8b3096514c96ad9e2106bf039e2fe34a704c587585b569a36df08c3c"},
+ {file = "fonttools-4.59.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8991bdbae39cf78bcc9cd3d81f6528df1f83f2e7c23ccf6f990fa1f0b6e19708"},
+ {file = "fonttools-4.59.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:53c1a411b7690042535a4f0edf2120096a39a506adeb6c51484a232e59f2aa0c"},
+ {file = "fonttools-4.59.2-cp314-cp314t-win32.whl", hash = "sha256:59d85088e29fa7a8f87d19e97a1beae2a35821ee48d8ef6d2c4f965f26cb9f8a"},
+ {file = "fonttools-4.59.2-cp314-cp314t-win_amd64.whl", hash = "sha256:7ad5d8d8cc9e43cb438b3eb4a0094dd6d4088daa767b0a24d52529361fd4c199"},
+ {file = "fonttools-4.59.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3cdf9d32690f0e235342055f0a6108eedfccf67b213b033bac747eb809809513"},
+ {file = "fonttools-4.59.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:67f9640d6b31d66c0bc54bdbe8ed50983c755521c101576a25e377a8711e8207"},
+ {file = "fonttools-4.59.2-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:464d15b58a9fd4304c728735fc1d42cd812fd9ebc27c45b18e78418efd337c28"},
+ {file = "fonttools-4.59.2-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a039c38d5644c691eb53cd65360921338f54e44c90b4e764605711e046c926ee"},
+ {file = "fonttools-4.59.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:e4f5100e66ec307cce8b52fc03e379b5d1596e9cb8d8b19dfeeccc1e68d86c96"},
+ {file = "fonttools-4.59.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:af6dbd463a3530256abf21f675ddf87646272bc48901803a185c49d06287fbf1"},
+ {file = "fonttools-4.59.2-cp39-cp39-win32.whl", hash = "sha256:594a6fd2f8296583ac7babc4880c8deee7c4f05ab0141addc6bce8b8e367e996"},
+ {file = "fonttools-4.59.2-cp39-cp39-win_amd64.whl", hash = "sha256:fc21c4a05226fd39715f66c1c28214862474db50df9f08fd1aa2f96698887bc3"},
+ {file = "fonttools-4.59.2-py3-none-any.whl", hash = "sha256:8bd0f759020e87bb5d323e6283914d9bf4ae35a7307dafb2cbd1e379e720ad37"},
+ {file = "fonttools-4.59.2.tar.gz", hash = "sha256:e72c0749b06113f50bcb80332364c6be83a9582d6e3db3fe0b280f996dc2ef22"},
+]
+
+[package.extras]
+all = ["brotli (>=1.0.1) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\"", "lxml (>=4.0)", "lz4 (>=1.7.4.2)", "matplotlib", "munkres ; platform_python_implementation == \"PyPy\"", "pycairo", "scipy ; platform_python_implementation != \"PyPy\"", "skia-pathops (>=0.5.0)", "sympy", "uharfbuzz (>=0.23.0)", "unicodedata2 (>=15.1.0) ; python_version <= \"3.12\"", "xattr ; sys_platform == \"darwin\"", "zopfli (>=0.1.4)"]
+graphite = ["lz4 (>=1.7.4.2)"]
+interpolatable = ["munkres ; platform_python_implementation == \"PyPy\"", "pycairo", "scipy ; platform_python_implementation != \"PyPy\""]
+lxml = ["lxml (>=4.0)"]
+pathops = ["skia-pathops (>=0.5.0)"]
+plot = ["matplotlib"]
+repacker = ["uharfbuzz (>=0.23.0)"]
+symfont = ["sympy"]
+type1 = ["xattr ; sys_platform == \"darwin\""]
+unicode = ["unicodedata2 (>=15.1.0) ; python_version <= \"3.12\""]
+woff = ["brotli (>=1.0.1) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\"", "zopfli (>=0.1.4)"]
+
+[[package]]
+name = "frozenlist"
+version = "1.7.0"
+description = "A list-like structure which implements collections.abc.MutableSequence"
+optional = false
+python-versions = ">=3.9"
+groups = ["main"]
+files = [
+ {file = "frozenlist-1.7.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cc4df77d638aa2ed703b878dd093725b72a824c3c546c076e8fdf276f78ee84a"},
+ {file = "frozenlist-1.7.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:716a9973a2cc963160394f701964fe25012600f3d311f60c790400b00e568b61"},
+ {file = "frozenlist-1.7.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a0fd1bad056a3600047fb9462cff4c5322cebc59ebf5d0a3725e0ee78955001d"},
+ {file = "frozenlist-1.7.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3789ebc19cb811163e70fe2bd354cea097254ce6e707ae42e56f45e31e96cb8e"},
+ {file = "frozenlist-1.7.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:af369aa35ee34f132fcfad5be45fbfcde0e3a5f6a1ec0712857f286b7d20cca9"},
+ {file = "frozenlist-1.7.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac64b6478722eeb7a3313d494f8342ef3478dff539d17002f849101b212ef97c"},
+ {file = "frozenlist-1.7.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f89f65d85774f1797239693cef07ad4c97fdd0639544bad9ac4b869782eb1981"},
+ {file = "frozenlist-1.7.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1073557c941395fdfcfac13eb2456cb8aad89f9de27bae29fabca8e563b12615"},
+ {file = "frozenlist-1.7.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ed8d2fa095aae4bdc7fdd80351009a48d286635edffee66bf865e37a9125c50"},
+ {file = "frozenlist-1.7.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:24c34bea555fe42d9f928ba0a740c553088500377448febecaa82cc3e88aa1fa"},
+ {file = "frozenlist-1.7.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:69cac419ac6a6baad202c85aaf467b65ac860ac2e7f2ac1686dc40dbb52f6577"},
+ {file = "frozenlist-1.7.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:960d67d0611f4c87da7e2ae2eacf7ea81a5be967861e0c63cf205215afbfac59"},
+ {file = "frozenlist-1.7.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:41be2964bd4b15bf575e5daee5a5ce7ed3115320fb3c2b71fca05582ffa4dc9e"},
+ {file = "frozenlist-1.7.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:46d84d49e00c9429238a7ce02dc0be8f6d7cd0cd405abd1bebdc991bf27c15bd"},
+ {file = "frozenlist-1.7.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:15900082e886edb37480335d9d518cec978afc69ccbc30bd18610b7c1b22a718"},
+ {file = "frozenlist-1.7.0-cp310-cp310-win32.whl", hash = "sha256:400ddd24ab4e55014bba442d917203c73b2846391dd42ca5e38ff52bb18c3c5e"},
+ {file = "frozenlist-1.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:6eb93efb8101ef39d32d50bce242c84bcbddb4f7e9febfa7b524532a239b4464"},
+ {file = "frozenlist-1.7.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:aa51e147a66b2d74de1e6e2cf5921890de6b0f4820b257465101d7f37b49fb5a"},
+ {file = "frozenlist-1.7.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9b35db7ce1cd71d36ba24f80f0c9e7cff73a28d7a74e91fe83e23d27c7828750"},
+ {file = "frozenlist-1.7.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:34a69a85e34ff37791e94542065c8416c1afbf820b68f720452f636d5fb990cd"},
+ {file = "frozenlist-1.7.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a646531fa8d82c87fe4bb2e596f23173caec9185bfbca5d583b4ccfb95183e2"},
+ {file = "frozenlist-1.7.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:79b2ffbba483f4ed36a0f236ccb85fbb16e670c9238313709638167670ba235f"},
+ {file = "frozenlist-1.7.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a26f205c9ca5829cbf82bb2a84b5c36f7184c4316617d7ef1b271a56720d6b30"},
+ {file = "frozenlist-1.7.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bcacfad3185a623fa11ea0e0634aac7b691aa925d50a440f39b458e41c561d98"},
+ {file = "frozenlist-1.7.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:72c1b0fe8fe451b34f12dce46445ddf14bd2a5bcad7e324987194dc8e3a74c86"},
+ {file = "frozenlist-1.7.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61d1a5baeaac6c0798ff6edfaeaa00e0e412d49946c53fae8d4b8e8b3566c4ae"},
+ {file = "frozenlist-1.7.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7edf5c043c062462f09b6820de9854bf28cc6cc5b6714b383149745e287181a8"},
+ {file = "frozenlist-1.7.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:d50ac7627b3a1bd2dcef6f9da89a772694ec04d9a61b66cf87f7d9446b4a0c31"},
+ {file = "frozenlist-1.7.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ce48b2fece5aeb45265bb7a58259f45027db0abff478e3077e12b05b17fb9da7"},
+ {file = "frozenlist-1.7.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:fe2365ae915a1fafd982c146754e1de6ab3478def8a59c86e1f7242d794f97d5"},
+ {file = "frozenlist-1.7.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:45a6f2fdbd10e074e8814eb98b05292f27bad7d1883afbe009d96abdcf3bc898"},
+ {file = "frozenlist-1.7.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:21884e23cffabb157a9dd7e353779077bf5b8f9a58e9b262c6caad2ef5f80a56"},
+ {file = "frozenlist-1.7.0-cp311-cp311-win32.whl", hash = "sha256:284d233a8953d7b24f9159b8a3496fc1ddc00f4db99c324bd5fb5f22d8698ea7"},
+ {file = "frozenlist-1.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:387cbfdcde2f2353f19c2f66bbb52406d06ed77519ac7ee21be0232147c2592d"},
+ {file = "frozenlist-1.7.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:3dbf9952c4bb0e90e98aec1bd992b3318685005702656bc6f67c1a32b76787f2"},
+ {file = "frozenlist-1.7.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:1f5906d3359300b8a9bb194239491122e6cf1444c2efb88865426f170c262cdb"},
+ {file = "frozenlist-1.7.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3dabd5a8f84573c8d10d8859a50ea2dec01eea372031929871368c09fa103478"},
+ {file = "frozenlist-1.7.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa57daa5917f1738064f302bf2626281a1cb01920c32f711fbc7bc36111058a8"},
+ {file = "frozenlist-1.7.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c193dda2b6d49f4c4398962810fa7d7c78f032bf45572b3e04dd5249dff27e08"},
+ {file = "frozenlist-1.7.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bfe2b675cf0aaa6d61bf8fbffd3c274b3c9b7b1623beb3809df8a81399a4a9c4"},
+ {file = "frozenlist-1.7.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8fc5d5cda37f62b262405cf9652cf0856839c4be8ee41be0afe8858f17f4c94b"},
+ {file = "frozenlist-1.7.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0d5ce521d1dd7d620198829b87ea002956e4319002ef0bc8d3e6d045cb4646e"},
+ {file = "frozenlist-1.7.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:488d0a7d6a0008ca0db273c542098a0fa9e7dfaa7e57f70acef43f32b3f69dca"},
+ {file = "frozenlist-1.7.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:15a7eaba63983d22c54d255b854e8108e7e5f3e89f647fc854bd77a237e767df"},
+ {file = "frozenlist-1.7.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:1eaa7e9c6d15df825bf255649e05bd8a74b04a4d2baa1ae46d9c2d00b2ca2cb5"},
+ {file = "frozenlist-1.7.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e4389e06714cfa9d47ab87f784a7c5be91d3934cd6e9a7b85beef808297cc025"},
+ {file = "frozenlist-1.7.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:73bd45e1488c40b63fe5a7df892baf9e2a4d4bb6409a2b3b78ac1c6236178e01"},
+ {file = "frozenlist-1.7.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:99886d98e1643269760e5fe0df31e5ae7050788dd288947f7f007209b8c33f08"},
+ {file = "frozenlist-1.7.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:290a172aae5a4c278c6da8a96222e6337744cd9c77313efe33d5670b9f65fc43"},
+ {file = "frozenlist-1.7.0-cp312-cp312-win32.whl", hash = "sha256:426c7bc70e07cfebc178bc4c2bf2d861d720c4fff172181eeb4a4c41d4ca2ad3"},
+ {file = "frozenlist-1.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:563b72efe5da92e02eb68c59cb37205457c977aa7a449ed1b37e6939e5c47c6a"},
+ {file = "frozenlist-1.7.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ee80eeda5e2a4e660651370ebffd1286542b67e268aa1ac8d6dbe973120ef7ee"},
+ {file = "frozenlist-1.7.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d1a81c85417b914139e3a9b995d4a1c84559afc839a93cf2cb7f15e6e5f6ed2d"},
+ {file = "frozenlist-1.7.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cbb65198a9132ebc334f237d7b0df163e4de83fb4f2bdfe46c1e654bdb0c5d43"},
+ {file = "frozenlist-1.7.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dab46c723eeb2c255a64f9dc05b8dd601fde66d6b19cdb82b2e09cc6ff8d8b5d"},
+ {file = "frozenlist-1.7.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6aeac207a759d0dedd2e40745575ae32ab30926ff4fa49b1635def65806fddee"},
+ {file = "frozenlist-1.7.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bd8c4e58ad14b4fa7802b8be49d47993182fdd4023393899632c88fd8cd994eb"},
+ {file = "frozenlist-1.7.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04fb24d104f425da3540ed83cbfc31388a586a7696142004c577fa61c6298c3f"},
+ {file = "frozenlist-1.7.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6a5c505156368e4ea6b53b5ac23c92d7edc864537ff911d2fb24c140bb175e60"},
+ {file = "frozenlist-1.7.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8bd7eb96a675f18aa5c553eb7ddc24a43c8c18f22e1f9925528128c052cdbe00"},
+ {file = "frozenlist-1.7.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:05579bf020096fe05a764f1f84cd104a12f78eaab68842d036772dc6d4870b4b"},
+ {file = "frozenlist-1.7.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:376b6222d114e97eeec13d46c486facd41d4f43bab626b7c3f6a8b4e81a5192c"},
+ {file = "frozenlist-1.7.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:0aa7e176ebe115379b5b1c95b4096fb1c17cce0847402e227e712c27bdb5a949"},
+ {file = "frozenlist-1.7.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3fbba20e662b9c2130dc771e332a99eff5da078b2b2648153a40669a6d0e36ca"},
+ {file = "frozenlist-1.7.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:f3f4410a0a601d349dd406b5713fec59b4cee7e71678d5b17edda7f4655a940b"},
+ {file = "frozenlist-1.7.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e2cdfaaec6a2f9327bf43c933c0319a7c429058e8537c508964a133dffee412e"},
+ {file = "frozenlist-1.7.0-cp313-cp313-win32.whl", hash = "sha256:5fc4df05a6591c7768459caba1b342d9ec23fa16195e744939ba5914596ae3e1"},
+ {file = "frozenlist-1.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:52109052b9791a3e6b5d1b65f4b909703984b770694d3eb64fad124c835d7cba"},
+ {file = "frozenlist-1.7.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:a6f86e4193bb0e235ef6ce3dde5cbabed887e0b11f516ce8a0f4d3b33078ec2d"},
+ {file = "frozenlist-1.7.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:82d664628865abeb32d90ae497fb93df398a69bb3434463d172b80fc25b0dd7d"},
+ {file = "frozenlist-1.7.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:912a7e8375a1c9a68325a902f3953191b7b292aa3c3fb0d71a216221deca460b"},
+ {file = "frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9537c2777167488d539bc5de2ad262efc44388230e5118868e172dd4a552b146"},
+ {file = "frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:f34560fb1b4c3e30ba35fa9a13894ba39e5acfc5f60f57d8accde65f46cc5e74"},
+ {file = "frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:acd03d224b0175f5a850edc104ac19040d35419eddad04e7cf2d5986d98427f1"},
+ {file = "frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2038310bc582f3d6a09b3816ab01737d60bf7b1ec70f5356b09e84fb7408ab1"},
+ {file = "frozenlist-1.7.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b8c05e4c8e5f36e5e088caa1bf78a687528f83c043706640a92cb76cd6999384"},
+ {file = "frozenlist-1.7.0-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:765bb588c86e47d0b68f23c1bee323d4b703218037765dcf3f25c838c6fecceb"},
+ {file = "frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:32dc2e08c67d86d0969714dd484fd60ff08ff81d1a1e40a77dd34a387e6ebc0c"},
+ {file = "frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:c0303e597eb5a5321b4de9c68e9845ac8f290d2ab3f3e2c864437d3c5a30cd65"},
+ {file = "frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:a47f2abb4e29b3a8d0b530f7c3598badc6b134562b1a5caee867f7c62fee51e3"},
+ {file = "frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:3d688126c242a6fabbd92e02633414d40f50bb6002fa4cf995a1d18051525657"},
+ {file = "frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:4e7e9652b3d367c7bd449a727dc79d5043f48b88d0cbfd4f9f1060cf2b414104"},
+ {file = "frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:1a85e345b4c43db8b842cab1feb41be5cc0b10a1830e6295b69d7310f99becaf"},
+ {file = "frozenlist-1.7.0-cp313-cp313t-win32.whl", hash = "sha256:3a14027124ddb70dfcee5148979998066897e79f89f64b13328595c4bdf77c81"},
+ {file = "frozenlist-1.7.0-cp313-cp313t-win_amd64.whl", hash = "sha256:3bf8010d71d4507775f658e9823210b7427be36625b387221642725b515dcf3e"},
+ {file = "frozenlist-1.7.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:cea3dbd15aea1341ea2de490574a4a37ca080b2ae24e4b4f4b51b9057b4c3630"},
+ {file = "frozenlist-1.7.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7d536ee086b23fecc36c2073c371572374ff50ef4db515e4e503925361c24f71"},
+ {file = "frozenlist-1.7.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:dfcebf56f703cb2e346315431699f00db126d158455e513bd14089d992101e44"},
+ {file = "frozenlist-1.7.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:974c5336e61d6e7eb1ea5b929cb645e882aadab0095c5a6974a111e6479f8878"},
+ {file = "frozenlist-1.7.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c70db4a0ab5ab20878432c40563573229a7ed9241506181bba12f6b7d0dc41cb"},
+ {file = "frozenlist-1.7.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1137b78384eebaf70560a36b7b229f752fb64d463d38d1304939984d5cb887b6"},
+ {file = "frozenlist-1.7.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e793a9f01b3e8b5c0bc646fb59140ce0efcc580d22a3468d70766091beb81b35"},
+ {file = "frozenlist-1.7.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:74739ba8e4e38221d2c5c03d90a7e542cb8ad681915f4ca8f68d04f810ee0a87"},
+ {file = "frozenlist-1.7.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e63344c4e929b1a01e29bc184bbb5fd82954869033765bfe8d65d09e336a677"},
+ {file = "frozenlist-1.7.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2ea2a7369eb76de2217a842f22087913cdf75f63cf1307b9024ab82dfb525938"},
+ {file = "frozenlist-1.7.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:836b42f472a0e006e02499cef9352ce8097f33df43baaba3e0a28a964c26c7d2"},
+ {file = "frozenlist-1.7.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e22b9a99741294b2571667c07d9f8cceec07cb92aae5ccda39ea1b6052ed4319"},
+ {file = "frozenlist-1.7.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:9a19e85cc503d958abe5218953df722748d87172f71b73cf3c9257a91b999890"},
+ {file = "frozenlist-1.7.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:f22dac33bb3ee8fe3e013aa7b91dc12f60d61d05b7fe32191ffa84c3aafe77bd"},
+ {file = "frozenlist-1.7.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:9ccec739a99e4ccf664ea0775149f2749b8a6418eb5b8384b4dc0a7d15d304cb"},
+ {file = "frozenlist-1.7.0-cp39-cp39-win32.whl", hash = "sha256:b3950f11058310008a87757f3eee16a8e1ca97979833239439586857bc25482e"},
+ {file = "frozenlist-1.7.0-cp39-cp39-win_amd64.whl", hash = "sha256:43a82fce6769c70f2f5a06248b614a7d268080a9d20f7457ef10ecee5af82b63"},
+ {file = "frozenlist-1.7.0-py3-none-any.whl", hash = "sha256:9a5af342e34f7e97caf8c995864c7a396418ae2859cc6fdf1b1073020d516a7e"},
+ {file = "frozenlist-1.7.0.tar.gz", hash = "sha256:2e310d81923c2437ea8670467121cc3e9b0f76d3043cc1d2331d56c7fb7a3a8f"},
+]
+
+[[package]]
+name = "fsspec"
+version = "2025.3.0"
+description = "File-system specification"
+optional = false
+python-versions = ">=3.8"
+groups = ["main"]
+files = [
+ {file = "fsspec-2025.3.0-py3-none-any.whl", hash = "sha256:efb87af3efa9103f94ca91a7f8cb7a4df91af9f74fc106c9c7ea0efd7277c1b3"},
+ {file = "fsspec-2025.3.0.tar.gz", hash = "sha256:a935fd1ea872591f2b5148907d103488fc523295e6c64b835cfad8c3eca44972"},
+]
+
+[package.dependencies]
+aiohttp = {version = "<4.0.0a0 || >4.0.0a0,<4.0.0a1 || >4.0.0a1", optional = true, markers = "extra == \"http\""}
+
+[package.extras]
+abfs = ["adlfs"]
+adl = ["adlfs"]
+arrow = ["pyarrow (>=1)"]
+dask = ["dask", "distributed"]
+dev = ["pre-commit", "ruff"]
+doc = ["numpydoc", "sphinx", "sphinx-design", "sphinx-rtd-theme", "yarl"]
+dropbox = ["dropbox", "dropboxdrivefs", "requests"]
+full = ["adlfs", "aiohttp (!=4.0.0a0,!=4.0.0a1)", "dask", "distributed", "dropbox", "dropboxdrivefs", "fusepy", "gcsfs", "libarchive-c", "ocifs", "panel", "paramiko", "pyarrow (>=1)", "pygit2", "requests", "s3fs", "smbprotocol", "tqdm"]
+fuse = ["fusepy"]
+gcs = ["gcsfs"]
+git = ["pygit2"]
+github = ["requests"]
+gs = ["gcsfs"]
+gui = ["panel"]
+hdfs = ["pyarrow (>=1)"]
+http = ["aiohttp (!=4.0.0a0,!=4.0.0a1)"]
+libarchive = ["libarchive-c"]
+oci = ["ocifs"]
+s3 = ["s3fs"]
+sftp = ["paramiko"]
+smb = ["smbprotocol"]
+ssh = ["paramiko"]
+test = ["aiohttp (!=4.0.0a0,!=4.0.0a1)", "numpy", "pytest", "pytest-asyncio (!=0.22.0)", "pytest-benchmark", "pytest-cov", "pytest-mock", "pytest-recording", "pytest-rerunfailures", "requests"]
+test-downstream = ["aiobotocore (>=2.5.4,<3.0.0)", "dask[dataframe,test]", "moto[server] (>4,<5)", "pytest-timeout", "xarray"]
+test-full = ["adlfs", "aiohttp (!=4.0.0a0,!=4.0.0a1)", "cloudpickle", "dask", "distributed", "dropbox", "dropboxdrivefs", "fastparquet", "fusepy", "gcsfs", "jinja2", "kerchunk", "libarchive-c", "lz4", "notebook", "numpy", "ocifs", "pandas", "panel", "paramiko", "pyarrow", "pyarrow (>=1)", "pyftpdlib", "pygit2", "pytest", "pytest-asyncio (!=0.22.0)", "pytest-benchmark", "pytest-cov", "pytest-mock", "pytest-recording", "pytest-rerunfailures", "python-snappy", "requests", "smbprotocol", "tqdm", "urllib3", "zarr", "zstandard"]
+tqdm = ["tqdm"]
+
+[[package]]
+name = "fuzzywuzzy"
+version = "0.18.0"
+description = "Fuzzy string matching in python"
+optional = false
+python-versions = "*"
+groups = ["main"]
+files = [
+ {file = "fuzzywuzzy-0.18.0-py2.py3-none-any.whl", hash = "sha256:928244b28db720d1e0ee7587acf660ea49d7e4c632569cad4f1cd7e68a5f0993"},
+ {file = "fuzzywuzzy-0.18.0.tar.gz", hash = "sha256:45016e92264780e58972dca1b3d939ac864b78437422beecebb3095f8efd00e8"},
+]
+
+[package.extras]
+speedup = ["python-levenshtein (>=0.12)"]
+
+[[package]]
+name = "gitdb"
+version = "4.0.12"
+description = "Git Object Database"
+optional = false
+python-versions = ">=3.7"
+groups = ["benchmarks"]
+files = [
+ {file = "gitdb-4.0.12-py3-none-any.whl", hash = "sha256:67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf"},
+ {file = "gitdb-4.0.12.tar.gz", hash = "sha256:5ef71f855d191a3326fcfbc0d5da835f26b13fbcba60c32c21091c349ffdb571"},
+]
+
+[package.dependencies]
+smmap = ">=3.0.1,<6"
+
+[[package]]
+name = "gitpython"
+version = "3.1.45"
+description = "GitPython is a Python library used to interact with Git repositories"
+optional = false
+python-versions = ">=3.7"
+groups = ["benchmarks"]
+files = [
+ {file = "gitpython-3.1.45-py3-none-any.whl", hash = "sha256:8908cb2e02fb3b93b7eb0f2827125cb699869470432cc885f019b8fd0fccff77"},
+ {file = "gitpython-3.1.45.tar.gz", hash = "sha256:85b0ee964ceddf211c41b9f27a49086010a190fd8132a24e21f362a4b36a791c"},
+]
+
+[package.dependencies]
+gitdb = ">=4.0.1,<5"
+typing-extensions = {version = ">=3.10.0.2", markers = "python_version < \"3.10\""}
+
+[package.extras]
+doc = ["sphinx (>=7.1.2,<7.2)", "sphinx-autodoc-typehints", "sphinx_rtd_theme"]
+test = ["coverage[toml]", "ddt (>=1.1.1,!=1.4.3)", "mock ; python_version < \"3.8\"", "mypy", "pre-commit", "pytest (>=7.3.1)", "pytest-cov", "pytest-instafail", "pytest-mock", "pytest-sugar", "typing-extensions ; python_version < \"3.11\""]
+
+[[package]]
+name = "hf-transfer"
+version = "0.1.9"
+description = "Speed up file transfers with the Hugging Face Hub."
+optional = false
+python-versions = ">=3.7"
+groups = ["main"]
+files = [
+ {file = "hf_transfer-0.1.9-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:6e94e8822da79573c9b6ae4d6b2f847c59a7a06c5327d7db20751b68538dc4f6"},
+ {file = "hf_transfer-0.1.9-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3ebc4ab9023414880c8b1d3c38174d1c9989eb5022d37e814fa91a3060123eb0"},
+ {file = "hf_transfer-0.1.9-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8674026f21ed369aa2a0a4b46000aca850fc44cd2b54af33a172ce5325b4fc82"},
+ {file = "hf_transfer-0.1.9-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3a736dfbb2c84f5a2c975478ad200c0c8bfcb58a25a35db402678fb87ce17fa4"},
+ {file = "hf_transfer-0.1.9-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:504b8427fd785dd8546d53b9fafe6e436bd7a3adf76b9dce556507650a7b4567"},
+ {file = "hf_transfer-0.1.9-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2c7fc1b85f4d0f76e452765d7648c9f4bfd0aedb9ced2ae1ebfece2d8cfaf8e2"},
+ {file = "hf_transfer-0.1.9-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d991376f0eac70a60f0cbc95602aa708a6f7c8617f28b4945c1431d67b8e3c8"},
+ {file = "hf_transfer-0.1.9-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:e6ac4eddcd99575ed3735ed911ddf9d1697e2bd13aa3f0ad7e3904dd4863842e"},
+ {file = "hf_transfer-0.1.9-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:57fd9880da1ee0f47250f735f791fab788f0aa1ee36afc49f761349869c8b4d9"},
+ {file = "hf_transfer-0.1.9-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:5d561f0520f493c66b016d99ceabe69c23289aa90be38dd802d2aef279f15751"},
+ {file = "hf_transfer-0.1.9-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a5b366d34cd449fe9b20ef25941e6eef0460a2f74e7389f02e673e1f88ebd538"},
+ {file = "hf_transfer-0.1.9-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:e66acf91df4a8b72f60223059df3003062a5ae111757187ed1a06750a30e911b"},
+ {file = "hf_transfer-0.1.9-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:8669dbcc7a3e2e8d61d42cd24da9c50d57770bd74b445c65123291ca842a7e7a"},
+ {file = "hf_transfer-0.1.9-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8fd0167c4407a3bc4cdd0307e65ada2294ec04f1813d8a69a5243e379b22e9d8"},
+ {file = "hf_transfer-0.1.9-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ee8b10afedcb75f71091bcc197c526a6ebf5c58bbbadb34fdeee6160f55f619f"},
+ {file = "hf_transfer-0.1.9-cp38-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5828057e313de59300dd1abb489444bc452efe3f479d3c55b31a8f680936ba42"},
+ {file = "hf_transfer-0.1.9-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fc6bd19e1cc177c66bdef15ef8636ad3bde79d5a4f608c158021153b4573509d"},
+ {file = "hf_transfer-0.1.9-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdca9bfb89e6f8f281890cc61a8aff2d3cecaff7e1a4d275574d96ca70098557"},
+ {file = "hf_transfer-0.1.9-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:89a23f58b7b7effbc047b8ca286f131b17728c99a9f972723323003ffd1bb916"},
+ {file = "hf_transfer-0.1.9-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:dc7fff1345980d6c0ebb92c811d24afa4b98b3e07ed070c8e38cc91fd80478c5"},
+ {file = "hf_transfer-0.1.9-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:1a6bd16c667ebe89a069ca163060127a794fa3a3525292c900b8c8cc47985b0d"},
+ {file = "hf_transfer-0.1.9-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:d2fde99d502093ade3ab1b53f80da18480e9902aa960dab7f74fb1b9e5bc5746"},
+ {file = "hf_transfer-0.1.9-cp38-abi3-win32.whl", hash = "sha256:435cc3cdc8524ce57b074032b8fd76eed70a4224d2091232fa6a8cef8fd6803e"},
+ {file = "hf_transfer-0.1.9-cp38-abi3-win_amd64.whl", hash = "sha256:16f208fc678911c37e11aa7b586bc66a37d02e636208f18b6bc53d29b5df40ad"},
+ {file = "hf_transfer-0.1.9.tar.gz", hash = "sha256:035572865dab29d17e783fbf1e84cf1cb24f3fcf8f1b17db1cfc7fdf139f02bf"},
+]
+
+[[package]]
+name = "hf-xet"
+version = "1.1.9"
+description = "Fast transfer of large files with the Hugging Face Hub."
+optional = false
+python-versions = ">=3.8"
+groups = ["main"]
+markers = "platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"arm64\" or platform_machine == \"aarch64\""
+files = [
+ {file = "hf_xet-1.1.9-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:a3b6215f88638dd7a6ff82cb4e738dcbf3d863bf667997c093a3c990337d1160"},
+ {file = "hf_xet-1.1.9-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:9b486de7a64a66f9a172f4b3e0dfe79c9f0a93257c501296a2521a13495a698a"},
+ {file = "hf_xet-1.1.9-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a4c5a840c2c4e6ec875ed13703a60e3523bc7f48031dfd750923b2a4d1a5fc3c"},
+ {file = "hf_xet-1.1.9-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:96a6139c9e44dad1c52c52520db0fffe948f6bce487cfb9d69c125f254bb3790"},
+ {file = "hf_xet-1.1.9-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ad1022e9a998e784c97b2173965d07fe33ee26e4594770b7785a8cc8f922cd95"},
+ {file = "hf_xet-1.1.9-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:86754c2d6d5afb11b0a435e6e18911a4199262fe77553f8c50d75e21242193ea"},
+ {file = "hf_xet-1.1.9-cp37-abi3-win_amd64.whl", hash = "sha256:5aad3933de6b725d61d51034e04174ed1dce7a57c63d530df0014dea15a40127"},
+ {file = "hf_xet-1.1.9.tar.gz", hash = "sha256:c99073ce404462e909f1d5839b2d14a3827b8fe75ed8aed551ba6609c026c803"},
+]
+
+[package.extras]
+tests = ["pytest"]
+
+[[package]]
+name = "huggingface-hub"
+version = "0.34.4"
+description = "Client library to download and publish models, datasets and other repos on the huggingface.co hub"
+optional = false
+python-versions = ">=3.8.0"
+groups = ["main"]
+files = [
+ {file = "huggingface_hub-0.34.4-py3-none-any.whl", hash = "sha256:9b365d781739c93ff90c359844221beef048403f1bc1f1c123c191257c3c890a"},
+ {file = "huggingface_hub-0.34.4.tar.gz", hash = "sha256:a4228daa6fb001be3f4f4bdaf9a0db00e1739235702848df00885c9b5742c85c"},
+]
+
+[package.dependencies]
+filelock = "*"
+fsspec = ">=2023.5.0"
+hf-xet = {version = ">=1.1.3,<2.0.0", markers = "platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"arm64\" or platform_machine == \"aarch64\""}
+packaging = ">=20.9"
+pyyaml = ">=5.1"
+requests = "*"
+tqdm = ">=4.42.1"
+typing-extensions = ">=3.7.4.3"
+
+[package.extras]
+all = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "authlib (>=1.3.2)", "fastapi", "gradio (>=4.0.0)", "httpx", "itsdangerous", "jedi", "libcst (>=1.4.0)", "mypy (==1.15.0) ; python_version >= \"3.9\"", "mypy (>=1.14.1,<1.15.0) ; python_version == \"3.8\"", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "ruff (>=0.9.0)", "soundfile", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"]
+cli = ["InquirerPy (==0.3.4)"]
+dev = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "authlib (>=1.3.2)", "fastapi", "gradio (>=4.0.0)", "httpx", "itsdangerous", "jedi", "libcst (>=1.4.0)", "mypy (==1.15.0) ; python_version >= \"3.9\"", "mypy (>=1.14.1,<1.15.0) ; python_version == \"3.8\"", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "ruff (>=0.9.0)", "soundfile", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"]
+fastai = ["fastai (>=2.4)", "fastcore (>=1.3.27)", "toml"]
+hf-transfer = ["hf-transfer (>=0.1.4)"]
+hf-xet = ["hf-xet (>=1.1.2,<2.0.0)"]
+inference = ["aiohttp"]
+mcp = ["aiohttp", "mcp (>=1.8.0)", "typer"]
+oauth = ["authlib (>=1.3.2)", "fastapi", "httpx", "itsdangerous"]
+quality = ["libcst (>=1.4.0)", "mypy (==1.15.0) ; python_version >= \"3.9\"", "mypy (>=1.14.1,<1.15.0) ; python_version == \"3.8\"", "ruff (>=0.9.0)"]
+tensorflow = ["graphviz", "pydot", "tensorflow"]
+tensorflow-testing = ["keras (<3.0)", "tensorflow"]
+testing = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "authlib (>=1.3.2)", "fastapi", "gradio (>=4.0.0)", "httpx", "itsdangerous", "jedi", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "soundfile", "urllib3 (<2.0)"]
+torch = ["safetensors[torch]", "torch"]
+typing = ["types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)"]
+
+[[package]]
+name = "identify"
+version = "2.6.13"
+description = "File identification library for Python"
+optional = false
+python-versions = ">=3.9"
+groups = ["dev"]
+files = [
+ {file = "identify-2.6.13-py2.py3-none-any.whl", hash = "sha256:60381139b3ae39447482ecc406944190f690d4a2997f2584062089848361b33b"},
+ {file = "identify-2.6.13.tar.gz", hash = "sha256:da8d6c828e773620e13bfa86ea601c5a5310ba4bcd65edf378198b56a1f9fb32"},
+]
+
+[package.extras]
+license = ["ukkonen"]
+
+[[package]]
+name = "idna"
+version = "3.10"
+description = "Internationalized Domain Names in Applications (IDNA)"
+optional = false
+python-versions = ">=3.6"
+groups = ["main", "benchmarks", "docs"]
+files = [
+ {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"},
+ {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"},
+]
+
+[package.extras]
+all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"]
+
+[[package]]
+name = "imagesize"
+version = "1.4.1"
+description = "Getting image size from png/jpeg/jpeg2000/gif file"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+groups = ["docs"]
+files = [
+ {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"},
+ {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"},
+]
+
+[[package]]
+name = "importlib-metadata"
+version = "8.7.0"
+description = "Read metadata from Python packages"
+optional = false
+python-versions = ">=3.9"
+groups = ["main", "docs"]
+files = [
+ {file = "importlib_metadata-8.7.0-py3-none-any.whl", hash = "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd"},
+ {file = "importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000"},
+]
+markers = {main = "python_version == \"3.9\" and platform_system == \"Linux\" and platform_machine == \"x86_64\"", docs = "python_version == \"3.9\""}
+
+[package.dependencies]
+zipp = ">=3.20"
+
+[package.extras]
+check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""]
+cover = ["pytest-cov"]
+doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
+enabler = ["pytest-enabler (>=2.2)"]
+perf = ["ipython"]
+test = ["flufl.flake8", "importlib_resources (>=1.3) ; python_version < \"3.9\"", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"]
+type = ["pytest-mypy"]
+
+[[package]]
+name = "importlib-resources"
+version = "6.5.2"
+description = "Read resources from Python packages"
+optional = false
+python-versions = ">=3.9"
+groups = ["main"]
+markers = "python_version == \"3.9\""
+files = [
+ {file = "importlib_resources-6.5.2-py3-none-any.whl", hash = "sha256:789cfdc3ed28c78b67a06acb8126751ced69a3d5f79c095a98298cd8a760ccec"},
+ {file = "importlib_resources-6.5.2.tar.gz", hash = "sha256:185f87adef5bcc288449d98fb4fba07cea78bc036455dd44c5fc4a2fe78fed2c"},
+]
+
+[package.dependencies]
+zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""}
+
+[package.extras]
+check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""]
+cover = ["pytest-cov"]
+doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
+enabler = ["pytest-enabler (>=2.2)"]
+test = ["jaraco.test (>=5.4)", "pytest (>=6,!=8.1.*)", "zipp (>=3.17)"]
+type = ["pytest-mypy"]
+
+[[package]]
+name = "iniconfig"
+version = "2.1.0"
+description = "brain-dead simple config-ini parsing"
+optional = false
+python-versions = ">=3.8"
+groups = ["dev"]
+files = [
+ {file = "iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760"},
+ {file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"},
+]
+
+[[package]]
+name = "isort"
+version = "5.12.0"
+description = "A Python utility / library to sort Python imports."
+optional = false
+python-versions = ">=3.8.0"
+groups = ["dev"]
+files = [
+ {file = "isort-5.12.0-py3-none-any.whl", hash = "sha256:f84c2818376e66cf843d497486ea8fed8700b340f308f076c6fb1229dff318b6"},
+ {file = "isort-5.12.0.tar.gz", hash = "sha256:8bef7dde241278824a6d83f44a544709b065191b95b6e50894bdc722fcba0504"},
+]
+
+[package.extras]
+colors = ["colorama (>=0.4.3)"]
+pipfile-deprecated-finder = ["pip-shims (>=0.5.2)", "pipreqs", "requirementslib"]
+plugins = ["setuptools"]
+requirements-deprecated-finder = ["pip-api", "pipreqs"]
+
+[[package]]
+name = "jieba"
+version = "0.42.1"
+description = "Chinese Words Segmentation Utilities"
+optional = false
+python-versions = "*"
+groups = ["main"]
+files = [
+ {file = "jieba-0.42.1.tar.gz", hash = "sha256:055ca12f62674fafed09427f176506079bc135638a14e23e25be909131928db2"},
+]
+
+[[package]]
+name = "jinja2"
+version = "3.1.6"
+description = "A very fast and expressive template engine."
+optional = false
+python-versions = ">=3.7"
+groups = ["main", "docs"]
+files = [
+ {file = "jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"},
+ {file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"},
+]
+
+[package.dependencies]
+MarkupSafe = ">=2.0"
+
+[package.extras]
+i18n = ["Babel (>=2.7)"]
+
+[[package]]
+name = "joblib"
+version = "1.5.2"
+description = "Lightweight pipelining with Python functions"
+optional = false
+python-versions = ">=3.9"
+groups = ["main"]
+files = [
+ {file = "joblib-1.5.2-py3-none-any.whl", hash = "sha256:4e1f0bdbb987e6d843c70cf43714cb276623def372df3c22fe5266b2670bc241"},
+ {file = "joblib-1.5.2.tar.gz", hash = "sha256:3faa5c39054b2f03ca547da9b2f52fde67c06240c31853f306aea97f13647b55"},
+]
+
+[[package]]
+name = "kiwisolver"
+version = "1.4.7"
+description = "A fast implementation of the Cassowary constraint solver"
+optional = false
+python-versions = ">=3.8"
+groups = ["main"]
+markers = "python_version < \"3.11\""
+files = [
+ {file = "kiwisolver-1.4.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8a9c83f75223d5e48b0bc9cb1bf2776cf01563e00ade8775ffe13b0b6e1af3a6"},
+ {file = "kiwisolver-1.4.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:58370b1ffbd35407444d57057b57da5d6549d2d854fa30249771775c63b5fe17"},
+ {file = "kiwisolver-1.4.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:aa0abdf853e09aff551db11fce173e2177d00786c688203f52c87ad7fcd91ef9"},
+ {file = "kiwisolver-1.4.7-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:8d53103597a252fb3ab8b5845af04c7a26d5e7ea8122303dd7a021176a87e8b9"},
+ {file = "kiwisolver-1.4.7-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:88f17c5ffa8e9462fb79f62746428dd57b46eb931698e42e990ad63103f35e6c"},
+ {file = "kiwisolver-1.4.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88a9ca9c710d598fd75ee5de59d5bda2684d9db36a9f50b6125eaea3969c2599"},
+ {file = "kiwisolver-1.4.7-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f4d742cb7af1c28303a51b7a27aaee540e71bb8e24f68c736f6f2ffc82f2bf05"},
+ {file = "kiwisolver-1.4.7-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e28c7fea2196bf4c2f8d46a0415c77a1c480cc0724722f23d7410ffe9842c407"},
+ {file = "kiwisolver-1.4.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e968b84db54f9d42046cf154e02911e39c0435c9801681e3fc9ce8a3c4130278"},
+ {file = "kiwisolver-1.4.7-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0c18ec74c0472de033e1bebb2911c3c310eef5649133dd0bedf2a169a1b269e5"},
+ {file = "kiwisolver-1.4.7-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8f0ea6da6d393d8b2e187e6a5e3fb81f5862010a40c3945e2c6d12ae45cfb2ad"},
+ {file = "kiwisolver-1.4.7-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:f106407dda69ae456dd1227966bf445b157ccc80ba0dff3802bb63f30b74e895"},
+ {file = "kiwisolver-1.4.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:84ec80df401cfee1457063732d90022f93951944b5b58975d34ab56bb150dfb3"},
+ {file = "kiwisolver-1.4.7-cp310-cp310-win32.whl", hash = "sha256:71bb308552200fb2c195e35ef05de12f0c878c07fc91c270eb3d6e41698c3bcc"},
+ {file = "kiwisolver-1.4.7-cp310-cp310-win_amd64.whl", hash = "sha256:44756f9fd339de0fb6ee4f8c1696cfd19b2422e0d70b4cefc1cc7f1f64045a8c"},
+ {file = "kiwisolver-1.4.7-cp310-cp310-win_arm64.whl", hash = "sha256:78a42513018c41c2ffd262eb676442315cbfe3c44eed82385c2ed043bc63210a"},
+ {file = "kiwisolver-1.4.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d2b0e12a42fb4e72d509fc994713d099cbb15ebf1103545e8a45f14da2dfca54"},
+ {file = "kiwisolver-1.4.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2a8781ac3edc42ea4b90bc23e7d37b665d89423818e26eb6df90698aa2287c95"},
+ {file = "kiwisolver-1.4.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:46707a10836894b559e04b0fd143e343945c97fd170d69a2d26d640b4e297935"},
+ {file = "kiwisolver-1.4.7-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ef97b8df011141c9b0f6caf23b29379f87dd13183c978a30a3c546d2c47314cb"},
+ {file = "kiwisolver-1.4.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ab58c12a2cd0fc769089e6d38466c46d7f76aced0a1f54c77652446733d2d02"},
+ {file = "kiwisolver-1.4.7-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:803b8e1459341c1bb56d1c5c010406d5edec8a0713a0945851290a7930679b51"},
+ {file = "kiwisolver-1.4.7-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f9a9e8a507420fe35992ee9ecb302dab68550dedc0da9e2880dd88071c5fb052"},
+ {file = "kiwisolver-1.4.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18077b53dc3bb490e330669a99920c5e6a496889ae8c63b58fbc57c3d7f33a18"},
+ {file = "kiwisolver-1.4.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6af936f79086a89b3680a280c47ea90b4df7047b5bdf3aa5c524bbedddb9e545"},
+ {file = "kiwisolver-1.4.7-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:3abc5b19d24af4b77d1598a585b8a719beb8569a71568b66f4ebe1fb0449460b"},
+ {file = "kiwisolver-1.4.7-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:933d4de052939d90afbe6e9d5273ae05fb836cc86c15b686edd4b3560cc0ee36"},
+ {file = "kiwisolver-1.4.7-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:65e720d2ab2b53f1f72fb5da5fb477455905ce2c88aaa671ff0a447c2c80e8e3"},
+ {file = "kiwisolver-1.4.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3bf1ed55088f214ba6427484c59553123fdd9b218a42bbc8c6496d6754b1e523"},
+ {file = "kiwisolver-1.4.7-cp311-cp311-win32.whl", hash = "sha256:4c00336b9dd5ad96d0a558fd18a8b6f711b7449acce4c157e7343ba92dd0cf3d"},
+ {file = "kiwisolver-1.4.7-cp311-cp311-win_amd64.whl", hash = "sha256:929e294c1ac1e9f615c62a4e4313ca1823ba37326c164ec720a803287c4c499b"},
+ {file = "kiwisolver-1.4.7-cp311-cp311-win_arm64.whl", hash = "sha256:e33e8fbd440c917106b237ef1a2f1449dfbb9b6f6e1ce17c94cd6a1e0d438376"},
+ {file = "kiwisolver-1.4.7-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:5360cc32706dab3931f738d3079652d20982511f7c0ac5711483e6eab08efff2"},
+ {file = "kiwisolver-1.4.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:942216596dc64ddb25adb215c3c783215b23626f8d84e8eff8d6d45c3f29f75a"},
+ {file = "kiwisolver-1.4.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:48b571ecd8bae15702e4f22d3ff6a0f13e54d3d00cd25216d5e7f658242065ee"},
+ {file = "kiwisolver-1.4.7-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ad42ba922c67c5f219097b28fae965e10045ddf145d2928bfac2eb2e17673640"},
+ {file = "kiwisolver-1.4.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:612a10bdae23404a72941a0fc8fa2660c6ea1217c4ce0dbcab8a8f6543ea9e7f"},
+ {file = "kiwisolver-1.4.7-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9e838bba3a3bac0fe06d849d29772eb1afb9745a59710762e4ba3f4cb8424483"},
+ {file = "kiwisolver-1.4.7-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:22f499f6157236c19f4bbbd472fa55b063db77a16cd74d49afe28992dff8c258"},
+ {file = "kiwisolver-1.4.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:693902d433cf585133699972b6d7c42a8b9f8f826ebcaf0132ff55200afc599e"},
+ {file = "kiwisolver-1.4.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4e77f2126c3e0b0d055f44513ed349038ac180371ed9b52fe96a32aa071a5107"},
+ {file = "kiwisolver-1.4.7-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:657a05857bda581c3656bfc3b20e353c232e9193eb167766ad2dc58b56504948"},
+ {file = "kiwisolver-1.4.7-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4bfa75a048c056a411f9705856abfc872558e33c055d80af6a380e3658766038"},
+ {file = "kiwisolver-1.4.7-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:34ea1de54beef1c104422d210c47c7d2a4999bdecf42c7b5718fbe59a4cac383"},
+ {file = "kiwisolver-1.4.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:90da3b5f694b85231cf93586dad5e90e2d71b9428f9aad96952c99055582f520"},
+ {file = "kiwisolver-1.4.7-cp312-cp312-win32.whl", hash = "sha256:18e0cca3e008e17fe9b164b55735a325140a5a35faad8de92dd80265cd5eb80b"},
+ {file = "kiwisolver-1.4.7-cp312-cp312-win_amd64.whl", hash = "sha256:58cb20602b18f86f83a5c87d3ee1c766a79c0d452f8def86d925e6c60fbf7bfb"},
+ {file = "kiwisolver-1.4.7-cp312-cp312-win_arm64.whl", hash = "sha256:f5a8b53bdc0b3961f8b6125e198617c40aeed638b387913bf1ce78afb1b0be2a"},
+ {file = "kiwisolver-1.4.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2e6039dcbe79a8e0f044f1c39db1986a1b8071051efba3ee4d74f5b365f5226e"},
+ {file = "kiwisolver-1.4.7-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a1ecf0ac1c518487d9d23b1cd7139a6a65bc460cd101ab01f1be82ecf09794b6"},
+ {file = "kiwisolver-1.4.7-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7ab9ccab2b5bd5702ab0803676a580fffa2aa178c2badc5557a84cc943fcf750"},
+ {file = "kiwisolver-1.4.7-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f816dd2277f8d63d79f9c8473a79fe54047bc0467754962840782c575522224d"},
+ {file = "kiwisolver-1.4.7-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf8bcc23ceb5a1b624572a1623b9f79d2c3b337c8c455405ef231933a10da379"},
+ {file = "kiwisolver-1.4.7-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dea0bf229319828467d7fca8c7c189780aa9ff679c94539eed7532ebe33ed37c"},
+ {file = "kiwisolver-1.4.7-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c06a4c7cf15ec739ce0e5971b26c93638730090add60e183530d70848ebdd34"},
+ {file = "kiwisolver-1.4.7-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:913983ad2deb14e66d83c28b632fd35ba2b825031f2fa4ca29675e665dfecbe1"},
+ {file = "kiwisolver-1.4.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5337ec7809bcd0f424c6b705ecf97941c46279cf5ed92311782c7c9c2026f07f"},
+ {file = "kiwisolver-1.4.7-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4c26ed10c4f6fa6ddb329a5120ba3b6db349ca192ae211e882970bfc9d91420b"},
+ {file = "kiwisolver-1.4.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c619b101e6de2222c1fcb0531e1b17bbffbe54294bfba43ea0d411d428618c27"},
+ {file = "kiwisolver-1.4.7-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:073a36c8273647592ea332e816e75ef8da5c303236ec0167196793eb1e34657a"},
+ {file = "kiwisolver-1.4.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3ce6b2b0231bda412463e152fc18335ba32faf4e8c23a754ad50ffa70e4091ee"},
+ {file = "kiwisolver-1.4.7-cp313-cp313-win32.whl", hash = "sha256:f4c9aee212bc89d4e13f58be11a56cc8036cabad119259d12ace14b34476fd07"},
+ {file = "kiwisolver-1.4.7-cp313-cp313-win_amd64.whl", hash = "sha256:8a3ec5aa8e38fc4c8af308917ce12c536f1c88452ce554027e55b22cbbfbff76"},
+ {file = "kiwisolver-1.4.7-cp313-cp313-win_arm64.whl", hash = "sha256:76c8094ac20ec259471ac53e774623eb62e6e1f56cd8690c67ce6ce4fcb05650"},
+ {file = "kiwisolver-1.4.7-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5d5abf8f8ec1f4e22882273c423e16cae834c36856cac348cfbfa68e01c40f3a"},
+ {file = "kiwisolver-1.4.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:aeb3531b196ef6f11776c21674dba836aeea9d5bd1cf630f869e3d90b16cfade"},
+ {file = "kiwisolver-1.4.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b7d755065e4e866a8086c9bdada157133ff466476a2ad7861828e17b6026e22c"},
+ {file = "kiwisolver-1.4.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:08471d4d86cbaec61f86b217dd938a83d85e03785f51121e791a6e6689a3be95"},
+ {file = "kiwisolver-1.4.7-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7bbfcb7165ce3d54a3dfbe731e470f65739c4c1f85bb1018ee912bae139e263b"},
+ {file = "kiwisolver-1.4.7-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5d34eb8494bea691a1a450141ebb5385e4b69d38bb8403b5146ad279f4b30fa3"},
+ {file = "kiwisolver-1.4.7-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9242795d174daa40105c1d86aba618e8eab7bf96ba8c3ee614da8302a9f95503"},
+ {file = "kiwisolver-1.4.7-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:a0f64a48bb81af7450e641e3fe0b0394d7381e342805479178b3d335d60ca7cf"},
+ {file = "kiwisolver-1.4.7-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:8e045731a5416357638d1700927529e2b8ab304811671f665b225f8bf8d8f933"},
+ {file = "kiwisolver-1.4.7-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:4322872d5772cae7369f8351da1edf255a604ea7087fe295411397d0cfd9655e"},
+ {file = "kiwisolver-1.4.7-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:e1631290ee9271dffe3062d2634c3ecac02c83890ada077d225e081aca8aab89"},
+ {file = "kiwisolver-1.4.7-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:edcfc407e4eb17e037bca59be0e85a2031a2ac87e4fed26d3e9df88b4165f92d"},
+ {file = "kiwisolver-1.4.7-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:4d05d81ecb47d11e7f8932bd8b61b720bf0b41199358f3f5e36d38e28f0532c5"},
+ {file = "kiwisolver-1.4.7-cp38-cp38-win32.whl", hash = "sha256:b38ac83d5f04b15e515fd86f312479d950d05ce2368d5413d46c088dda7de90a"},
+ {file = "kiwisolver-1.4.7-cp38-cp38-win_amd64.whl", hash = "sha256:d83db7cde68459fc803052a55ace60bea2bae361fc3b7a6d5da07e11954e4b09"},
+ {file = "kiwisolver-1.4.7-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3f9362ecfca44c863569d3d3c033dbe8ba452ff8eed6f6b5806382741a1334bd"},
+ {file = "kiwisolver-1.4.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e8df2eb9b2bac43ef8b082e06f750350fbbaf2887534a5be97f6cf07b19d9583"},
+ {file = "kiwisolver-1.4.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f32d6edbc638cde7652bd690c3e728b25332acbadd7cad670cc4a02558d9c417"},
+ {file = "kiwisolver-1.4.7-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:e2e6c39bd7b9372b0be21456caab138e8e69cc0fc1190a9dfa92bd45a1e6e904"},
+ {file = "kiwisolver-1.4.7-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:dda56c24d869b1193fcc763f1284b9126550eaf84b88bbc7256e15028f19188a"},
+ {file = "kiwisolver-1.4.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79849239c39b5e1fd906556c474d9b0439ea6792b637511f3fe3a41158d89ca8"},
+ {file = "kiwisolver-1.4.7-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5e3bc157fed2a4c02ec468de4ecd12a6e22818d4f09cde2c31ee3226ffbefab2"},
+ {file = "kiwisolver-1.4.7-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3da53da805b71e41053dc670f9a820d1157aae77b6b944e08024d17bcd51ef88"},
+ {file = "kiwisolver-1.4.7-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:8705f17dfeb43139a692298cb6637ee2e59c0194538153e83e9ee0c75c2eddde"},
+ {file = "kiwisolver-1.4.7-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:82a5c2f4b87c26bb1a0ef3d16b5c4753434633b83d365cc0ddf2770c93829e3c"},
+ {file = "kiwisolver-1.4.7-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ce8be0466f4c0d585cdb6c1e2ed07232221df101a4c6f28821d2aa754ca2d9e2"},
+ {file = "kiwisolver-1.4.7-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:409afdfe1e2e90e6ee7fc896f3df9a7fec8e793e58bfa0d052c8a82f99c37abb"},
+ {file = "kiwisolver-1.4.7-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5b9c3f4ee0b9a439d2415012bd1b1cc2df59e4d6a9939f4d669241d30b414327"},
+ {file = "kiwisolver-1.4.7-cp39-cp39-win32.whl", hash = "sha256:a79ae34384df2b615eefca647a2873842ac3b596418032bef9a7283675962644"},
+ {file = "kiwisolver-1.4.7-cp39-cp39-win_amd64.whl", hash = "sha256:cf0438b42121a66a3a667de17e779330fc0f20b0d97d59d2f2121e182b0505e4"},
+ {file = "kiwisolver-1.4.7-cp39-cp39-win_arm64.whl", hash = "sha256:764202cc7e70f767dab49e8df52c7455e8de0df5d858fa801a11aa0d882ccf3f"},
+ {file = "kiwisolver-1.4.7-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:94252291e3fe68001b1dd747b4c0b3be12582839b95ad4d1b641924d68fd4643"},
+ {file = "kiwisolver-1.4.7-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5b7dfa3b546da08a9f622bb6becdb14b3e24aaa30adba66749d38f3cc7ea9706"},
+ {file = "kiwisolver-1.4.7-pp310-pypy310_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd3de6481f4ed8b734da5df134cd5a6a64fe32124fe83dde1e5b5f29fe30b1e6"},
+ {file = "kiwisolver-1.4.7-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a91b5f9f1205845d488c928e8570dcb62b893372f63b8b6e98b863ebd2368ff2"},
+ {file = "kiwisolver-1.4.7-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40fa14dbd66b8b8f470d5fc79c089a66185619d31645f9b0773b88b19f7223c4"},
+ {file = "kiwisolver-1.4.7-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:eb542fe7933aa09d8d8f9d9097ef37532a7df6497819d16efe4359890a2f417a"},
+ {file = "kiwisolver-1.4.7-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:bfa1acfa0c54932d5607e19a2c24646fb4c1ae2694437789129cf099789a3b00"},
+ {file = "kiwisolver-1.4.7-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:eee3ea935c3d227d49b4eb85660ff631556841f6e567f0f7bda972df6c2c9935"},
+ {file = "kiwisolver-1.4.7-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f3160309af4396e0ed04db259c3ccbfdc3621b5559b5453075e5de555e1f3a1b"},
+ {file = "kiwisolver-1.4.7-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a17f6a29cf8935e587cc8a4dbfc8368c55edc645283db0ce9801016f83526c2d"},
+ {file = "kiwisolver-1.4.7-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:10849fb2c1ecbfae45a693c070e0320a91b35dd4bcf58172c023b994283a124d"},
+ {file = "kiwisolver-1.4.7-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:ac542bf38a8a4be2dc6b15248d36315ccc65f0743f7b1a76688ffb6b5129a5c2"},
+ {file = "kiwisolver-1.4.7-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:8b01aac285f91ca889c800042c35ad3b239e704b150cfd3382adfc9dcc780e39"},
+ {file = "kiwisolver-1.4.7-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:48be928f59a1f5c8207154f935334d374e79f2b5d212826307d072595ad76a2e"},
+ {file = "kiwisolver-1.4.7-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f37cfe618a117e50d8c240555331160d73d0411422b59b5ee217843d7b693608"},
+ {file = "kiwisolver-1.4.7-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:599b5c873c63a1f6ed7eead644a8a380cfbdf5db91dcb6f85707aaab213b1674"},
+ {file = "kiwisolver-1.4.7-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:801fa7802e5cfabe3ab0c81a34c323a319b097dfb5004be950482d882f3d7225"},
+ {file = "kiwisolver-1.4.7-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:0c6c43471bc764fad4bc99c5c2d6d16a676b1abf844ca7c8702bdae92df01ee0"},
+ {file = "kiwisolver-1.4.7.tar.gz", hash = "sha256:9893ff81bd7107f7b685d3017cc6583daadb4fc26e4a888350df530e41980a60"},
+]
+
+[[package]]
+name = "kiwisolver"
+version = "1.4.9"
+description = "A fast implementation of the Cassowary constraint solver"
+optional = false
+python-versions = ">=3.10"
+groups = ["main"]
+markers = "python_version >= \"3.11\""
+files = [
+ {file = "kiwisolver-1.4.9-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b4b4d74bda2b8ebf4da5bd42af11d02d04428b2c32846e4c2c93219df8a7987b"},
+ {file = "kiwisolver-1.4.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:fb3b8132019ea572f4611d770991000d7f58127560c4889729248eb5852a102f"},
+ {file = "kiwisolver-1.4.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:84fd60810829c27ae375114cd379da1fa65e6918e1da405f356a775d49a62bcf"},
+ {file = "kiwisolver-1.4.9-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b78efa4c6e804ecdf727e580dbb9cba85624d2e1c6b5cb059c66290063bd99a9"},
+ {file = "kiwisolver-1.4.9-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d4efec7bcf21671db6a3294ff301d2fc861c31faa3c8740d1a94689234d1b415"},
+ {file = "kiwisolver-1.4.9-cp310-cp310-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:90f47e70293fc3688b71271100a1a5453aa9944a81d27ff779c108372cf5567b"},
+ {file = "kiwisolver-1.4.9-cp310-cp310-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8fdca1def57a2e88ef339de1737a1449d6dbf5fab184c54a1fca01d541317154"},
+ {file = "kiwisolver-1.4.9-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:9cf554f21be770f5111a1690d42313e140355e687e05cf82cb23d0a721a64a48"},
+ {file = "kiwisolver-1.4.9-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fc1795ac5cd0510207482c3d1d3ed781143383b8cfd36f5c645f3897ce066220"},
+ {file = "kiwisolver-1.4.9-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:ccd09f20ccdbbd341b21a67ab50a119b64a403b09288c27481575105283c1586"},
+ {file = "kiwisolver-1.4.9-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:540c7c72324d864406a009d72f5d6856f49693db95d1fbb46cf86febef873634"},
+ {file = "kiwisolver-1.4.9-cp310-cp310-win_amd64.whl", hash = "sha256:ede8c6d533bc6601a47ad4046080d36b8fc99f81e6f1c17b0ac3c2dc91ac7611"},
+ {file = "kiwisolver-1.4.9-cp310-cp310-win_arm64.whl", hash = "sha256:7b4da0d01ac866a57dd61ac258c5607b4cd677f63abaec7b148354d2b2cdd536"},
+ {file = "kiwisolver-1.4.9-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:eb14a5da6dc7642b0f3a18f13654847cd8b7a2550e2645a5bda677862b03ba16"},
+ {file = "kiwisolver-1.4.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:39a219e1c81ae3b103643d2aedb90f1ef22650deb266ff12a19e7773f3e5f089"},
+ {file = "kiwisolver-1.4.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2405a7d98604b87f3fc28b1716783534b1b4b8510d8142adca34ee0bc3c87543"},
+ {file = "kiwisolver-1.4.9-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:dc1ae486f9abcef254b5618dfb4113dd49f94c68e3e027d03cf0143f3f772b61"},
+ {file = "kiwisolver-1.4.9-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8a1f570ce4d62d718dce3f179ee78dac3b545ac16c0c04bb363b7607a949c0d1"},
+ {file = "kiwisolver-1.4.9-cp311-cp311-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cb27e7b78d716c591e88e0a09a2139c6577865d7f2e152488c2cc6257f460872"},
+ {file = "kiwisolver-1.4.9-cp311-cp311-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:15163165efc2f627eb9687ea5f3a28137217d217ac4024893d753f46bce9de26"},
+ {file = "kiwisolver-1.4.9-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bdee92c56a71d2b24c33a7d4c2856bd6419d017e08caa7802d2963870e315028"},
+ {file = "kiwisolver-1.4.9-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:412f287c55a6f54b0650bd9b6dce5aceddb95864a1a90c87af16979d37c89771"},
+ {file = "kiwisolver-1.4.9-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:2c93f00dcba2eea70af2be5f11a830a742fe6b579a1d4e00f47760ef13be247a"},
+ {file = "kiwisolver-1.4.9-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f117e1a089d9411663a3207ba874f31be9ac8eaa5b533787024dc07aeb74f464"},
+ {file = "kiwisolver-1.4.9-cp311-cp311-win_amd64.whl", hash = "sha256:be6a04e6c79819c9a8c2373317d19a96048e5a3f90bec587787e86a1153883c2"},
+ {file = "kiwisolver-1.4.9-cp311-cp311-win_arm64.whl", hash = "sha256:0ae37737256ba2de764ddc12aed4956460277f00c4996d51a197e72f62f5eec7"},
+ {file = "kiwisolver-1.4.9-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ac5a486ac389dddcc5bef4f365b6ae3ffff2c433324fb38dd35e3fab7c957999"},
+ {file = "kiwisolver-1.4.9-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f2ba92255faa7309d06fe44c3a4a97efe1c8d640c2a79a5ef728b685762a6fd2"},
+ {file = "kiwisolver-1.4.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4a2899935e724dd1074cb568ce7ac0dce28b2cd6ab539c8e001a8578eb106d14"},
+ {file = "kiwisolver-1.4.9-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f6008a4919fdbc0b0097089f67a1eb55d950ed7e90ce2cc3e640abadd2757a04"},
+ {file = "kiwisolver-1.4.9-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:67bb8b474b4181770f926f7b7d2f8c0248cbcb78b660fdd41a47054b28d2a752"},
+ {file = "kiwisolver-1.4.9-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2327a4a30d3ee07d2fbe2e7933e8a37c591663b96ce42a00bc67461a87d7df77"},
+ {file = "kiwisolver-1.4.9-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7a08b491ec91b1d5053ac177afe5290adacf1f0f6307d771ccac5de30592d198"},
+ {file = "kiwisolver-1.4.9-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d8fc5c867c22b828001b6a38d2eaeb88160bf5783c6cb4a5e440efc981ce286d"},
+ {file = "kiwisolver-1.4.9-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:3b3115b2581ea35bb6d1f24a4c90af37e5d9b49dcff267eeed14c3893c5b86ab"},
+ {file = "kiwisolver-1.4.9-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:858e4c22fb075920b96a291928cb7dea5644e94c0ee4fcd5af7e865655e4ccf2"},
+ {file = "kiwisolver-1.4.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ed0fecd28cc62c54b262e3736f8bb2512d8dcfdc2bcf08be5f47f96bf405b145"},
+ {file = "kiwisolver-1.4.9-cp312-cp312-win_amd64.whl", hash = "sha256:f68208a520c3d86ea51acf688a3e3002615a7f0238002cccc17affecc86a8a54"},
+ {file = "kiwisolver-1.4.9-cp312-cp312-win_arm64.whl", hash = "sha256:2c1a4f57df73965f3f14df20b80ee29e6a7930a57d2d9e8491a25f676e197c60"},
+ {file = "kiwisolver-1.4.9-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a5d0432ccf1c7ab14f9949eec60c5d1f924f17c037e9f8b33352fa05799359b8"},
+ {file = "kiwisolver-1.4.9-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efb3a45b35622bb6c16dbfab491a8f5a391fe0e9d45ef32f4df85658232ca0e2"},
+ {file = "kiwisolver-1.4.9-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1a12cf6398e8a0a001a059747a1cbf24705e18fe413bc22de7b3d15c67cffe3f"},
+ {file = "kiwisolver-1.4.9-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b67e6efbf68e077dd71d1a6b37e43e1a99d0bff1a3d51867d45ee8908b931098"},
+ {file = "kiwisolver-1.4.9-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5656aa670507437af0207645273ccdfee4f14bacd7f7c67a4306d0dcaeaf6eed"},
+ {file = "kiwisolver-1.4.9-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:bfc08add558155345129c7803b3671cf195e6a56e7a12f3dde7c57d9b417f525"},
+ {file = "kiwisolver-1.4.9-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:40092754720b174e6ccf9e845d0d8c7d8e12c3d71e7fc35f55f3813e96376f78"},
+ {file = "kiwisolver-1.4.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:497d05f29a1300d14e02e6441cf0f5ee81c1ff5a304b0d9fb77423974684e08b"},
+ {file = "kiwisolver-1.4.9-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:bdd1a81a1860476eb41ac4bc1e07b3f07259e6d55bbf739b79c8aaedcf512799"},
+ {file = "kiwisolver-1.4.9-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:e6b93f13371d341afee3be9f7c5964e3fe61d5fa30f6a30eb49856935dfe4fc3"},
+ {file = "kiwisolver-1.4.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d75aa530ccfaa593da12834b86a0724f58bff12706659baa9227c2ccaa06264c"},
+ {file = "kiwisolver-1.4.9-cp313-cp313-win_amd64.whl", hash = "sha256:dd0a578400839256df88c16abddf9ba14813ec5f21362e1fe65022e00c883d4d"},
+ {file = "kiwisolver-1.4.9-cp313-cp313-win_arm64.whl", hash = "sha256:d4188e73af84ca82468f09cadc5ac4db578109e52acb4518d8154698d3a87ca2"},
+ {file = "kiwisolver-1.4.9-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:5a0f2724dfd4e3b3ac5a82436a8e6fd16baa7d507117e4279b660fe8ca38a3a1"},
+ {file = "kiwisolver-1.4.9-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:1b11d6a633e4ed84fc0ddafd4ebfd8ea49b3f25082c04ad12b8315c11d504dc1"},
+ {file = "kiwisolver-1.4.9-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61874cdb0a36016354853593cffc38e56fc9ca5aa97d2c05d3dcf6922cd55a11"},
+ {file = "kiwisolver-1.4.9-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:60c439763a969a6af93b4881db0eed8fadf93ee98e18cbc35bc8da868d0c4f0c"},
+ {file = "kiwisolver-1.4.9-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92a2f997387a1b79a75e7803aa7ded2cfbe2823852ccf1ba3bcf613b62ae3197"},
+ {file = "kiwisolver-1.4.9-cp313-cp313t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a31d512c812daea6d8b3be3b2bfcbeb091dbb09177706569bcfc6240dcf8b41c"},
+ {file = "kiwisolver-1.4.9-cp313-cp313t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:52a15b0f35dad39862d376df10c5230155243a2c1a436e39eb55623ccbd68185"},
+ {file = "kiwisolver-1.4.9-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a30fd6fdef1430fd9e1ba7b3398b5ee4e2887783917a687d86ba69985fb08748"},
+ {file = "kiwisolver-1.4.9-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:cc9617b46837c6468197b5945e196ee9ca43057bb7d9d1ae688101e4e1dddf64"},
+ {file = "kiwisolver-1.4.9-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:0ab74e19f6a2b027ea4f845a78827969af45ce790e6cb3e1ebab71bdf9f215ff"},
+ {file = "kiwisolver-1.4.9-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dba5ee5d3981160c28d5490f0d1b7ed730c22470ff7f6cc26cfcfaacb9896a07"},
+ {file = "kiwisolver-1.4.9-cp313-cp313t-win_arm64.whl", hash = "sha256:0749fd8f4218ad2e851e11cc4dc05c7cbc0cbc4267bdfdb31782e65aace4ee9c"},
+ {file = "kiwisolver-1.4.9-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:9928fe1eb816d11ae170885a74d074f57af3a0d65777ca47e9aeb854a1fba386"},
+ {file = "kiwisolver-1.4.9-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:d0005b053977e7b43388ddec89fa567f43d4f6d5c2c0affe57de5ebf290dc552"},
+ {file = "kiwisolver-1.4.9-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2635d352d67458b66fd0667c14cb1d4145e9560d503219034a18a87e971ce4f3"},
+ {file = "kiwisolver-1.4.9-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:767c23ad1c58c9e827b649a9ab7809fd5fd9db266a9cf02b0e926ddc2c680d58"},
+ {file = "kiwisolver-1.4.9-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:72d0eb9fba308b8311685c2268cf7d0a0639a6cd027d8128659f72bdd8a024b4"},
+ {file = "kiwisolver-1.4.9-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f68e4f3eeca8fb22cc3d731f9715a13b652795ef657a13df1ad0c7dc0e9731df"},
+ {file = "kiwisolver-1.4.9-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d84cd4061ae292d8ac367b2c3fa3aad11cb8625a95d135fe93f286f914f3f5a6"},
+ {file = "kiwisolver-1.4.9-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a60ea74330b91bd22a29638940d115df9dc00af5035a9a2a6ad9399ffb4ceca5"},
+ {file = "kiwisolver-1.4.9-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:ce6a3a4e106cf35c2d9c4fa17c05ce0b180db622736845d4315519397a77beaf"},
+ {file = "kiwisolver-1.4.9-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:77937e5e2a38a7b48eef0585114fe7930346993a88060d0bf886086d2aa49ef5"},
+ {file = "kiwisolver-1.4.9-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:24c175051354f4a28c5d6a31c93906dc653e2bf234e8a4bbfb964892078898ce"},
+ {file = "kiwisolver-1.4.9-cp314-cp314-win_amd64.whl", hash = "sha256:0763515d4df10edf6d06a3c19734e2566368980d21ebec439f33f9eb936c07b7"},
+ {file = "kiwisolver-1.4.9-cp314-cp314-win_arm64.whl", hash = "sha256:0e4e2bf29574a6a7b7f6cb5fa69293b9f96c928949ac4a53ba3f525dffb87f9c"},
+ {file = "kiwisolver-1.4.9-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:d976bbb382b202f71c67f77b0ac11244021cfa3f7dfd9e562eefcea2df711548"},
+ {file = "kiwisolver-1.4.9-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2489e4e5d7ef9a1c300a5e0196e43d9c739f066ef23270607d45aba368b91f2d"},
+ {file = "kiwisolver-1.4.9-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:e2ea9f7ab7fbf18fffb1b5434ce7c69a07582f7acc7717720f1d69f3e806f90c"},
+ {file = "kiwisolver-1.4.9-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b34e51affded8faee0dfdb705416153819d8ea9250bbbf7ea1b249bdeb5f1122"},
+ {file = "kiwisolver-1.4.9-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d8aacd3d4b33b772542b2e01beb50187536967b514b00003bdda7589722d2a64"},
+ {file = "kiwisolver-1.4.9-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7cf974dd4e35fa315563ac99d6287a1024e4dc2077b8a7d7cd3d2fb65d283134"},
+ {file = "kiwisolver-1.4.9-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:85bd218b5ecfbee8c8a82e121802dcb519a86044c9c3b2e4aef02fa05c6da370"},
+ {file = "kiwisolver-1.4.9-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:0856e241c2d3df4efef7c04a1e46b1936b6120c9bcf36dd216e3acd84bc4fb21"},
+ {file = "kiwisolver-1.4.9-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:9af39d6551f97d31a4deebeac6f45b156f9755ddc59c07b402c148f5dbb6482a"},
+ {file = "kiwisolver-1.4.9-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:bb4ae2b57fc1d8cbd1cf7b1d9913803681ffa903e7488012be5b76dedf49297f"},
+ {file = "kiwisolver-1.4.9-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:aedff62918805fb62d43a4aa2ecd4482c380dc76cd31bd7c8878588a61bd0369"},
+ {file = "kiwisolver-1.4.9-cp314-cp314t-win_amd64.whl", hash = "sha256:1fa333e8b2ce4d9660f2cda9c0e1b6bafcfb2457a9d259faa82289e73ec24891"},
+ {file = "kiwisolver-1.4.9-cp314-cp314t-win_arm64.whl", hash = "sha256:4a48a2ce79d65d363597ef7b567ce3d14d68783d2b2263d98db3d9477805ba32"},
+ {file = "kiwisolver-1.4.9-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:4d1d9e582ad4d63062d34077a9a1e9f3c34088a2ec5135b1f7190c07cf366527"},
+ {file = "kiwisolver-1.4.9-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:deed0c7258ceb4c44ad5ec7d9918f9f14fd05b2be86378d86cf50e63d1e7b771"},
+ {file = "kiwisolver-1.4.9-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0a590506f303f512dff6b7f75fd2fd18e16943efee932008fe7140e5fa91d80e"},
+ {file = "kiwisolver-1.4.9-pp310-pypy310_pp73-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e09c2279a4d01f099f52d5c4b3d9e208e91edcbd1a175c9662a8b16e000fece9"},
+ {file = "kiwisolver-1.4.9-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c9e7cdf45d594ee04d5be1b24dd9d49f3d1590959b2271fb30b5ca2b262c00fb"},
+ {file = "kiwisolver-1.4.9-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:720e05574713db64c356e86732c0f3c5252818d05f9df320f0ad8380641acea5"},
+ {file = "kiwisolver-1.4.9-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:17680d737d5335b552994a2008fab4c851bcd7de33094a82067ef3a576ff02fa"},
+ {file = "kiwisolver-1.4.9-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:85b5352f94e490c028926ea567fc569c52ec79ce131dadb968d3853e809518c2"},
+ {file = "kiwisolver-1.4.9-pp311-pypy311_pp73-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:464415881e4801295659462c49461a24fb107c140de781d55518c4b80cb6790f"},
+ {file = "kiwisolver-1.4.9-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:fb940820c63a9590d31d88b815e7a3aa5915cad3ce735ab45f0c730b39547de1"},
+ {file = "kiwisolver-1.4.9.tar.gz", hash = "sha256:c3b22c26c6fd6811b0ae8363b95ca8ce4ea3c202d3d0975b2914310ceb1bcc4d"},
+]
+
+[[package]]
+name = "lazy-object-proxy"
+version = "1.12.0"
+description = "A fast and thorough lazy object proxy."
+optional = false
+python-versions = ">=3.9"
+groups = ["dev"]
+files = [
+ {file = "lazy_object_proxy-1.12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:61d5e3310a4aa5792c2b599a7a78ccf8687292c8eb09cf187cca8f09cf6a7519"},
+ {file = "lazy_object_proxy-1.12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1ca33565f698ac1aece152a10f432415d1a2aa9a42dfe23e5ba2bc255ab91f6"},
+ {file = "lazy_object_proxy-1.12.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d01c7819a410f7c255b20799b65d36b414379a30c6f1684c7bd7eb6777338c1b"},
+ {file = "lazy_object_proxy-1.12.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:029d2b355076710505c9545aef5ab3f750d89779310e26ddf2b7b23f6ea03cd8"},
+ {file = "lazy_object_proxy-1.12.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cc6e3614eca88b1c8a625fc0a47d0d745e7c3255b21dac0e30b3037c5e3deeb8"},
+ {file = "lazy_object_proxy-1.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:be5fe974e39ceb0d6c9db0663c0464669cf866b2851c73971409b9566e880eab"},
+ {file = "lazy_object_proxy-1.12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1cf69cd1a6c7fe2dbcc3edaa017cf010f4192e53796538cc7d5e1fedbfa4bcff"},
+ {file = "lazy_object_proxy-1.12.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:efff4375a8c52f55a145dc8487a2108c2140f0bec4151ab4e1843e52eb9987ad"},
+ {file = "lazy_object_proxy-1.12.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1192e8c2f1031a6ff453ee40213afa01ba765b3dc861302cd91dbdb2e2660b00"},
+ {file = "lazy_object_proxy-1.12.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3605b632e82a1cbc32a1e5034278a64db555b3496e0795723ee697006b980508"},
+ {file = "lazy_object_proxy-1.12.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a61095f5d9d1a743e1e20ec6d6db6c2ca511961777257ebd9b288951b23b44fa"},
+ {file = "lazy_object_proxy-1.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:997b1d6e10ecc6fb6fe0f2c959791ae59599f41da61d652f6c903d1ee58b7370"},
+ {file = "lazy_object_proxy-1.12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8ee0d6027b760a11cc18281e702c0309dd92da458a74b4c15025d7fc490deede"},
+ {file = "lazy_object_proxy-1.12.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:4ab2c584e3cc8be0dfca422e05ad30a9abe3555ce63e9ab7a559f62f8dbc6ff9"},
+ {file = "lazy_object_proxy-1.12.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:14e348185adbd03ec17d051e169ec45686dcd840a3779c9d4c10aabe2ca6e1c0"},
+ {file = "lazy_object_proxy-1.12.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c4fcbe74fb85df8ba7825fa05eddca764138da752904b378f0ae5ab33a36c308"},
+ {file = "lazy_object_proxy-1.12.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:563d2ec8e4d4b68ee7848c5ab4d6057a6d703cb7963b342968bb8758dda33a23"},
+ {file = "lazy_object_proxy-1.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:53c7fd99eb156bbb82cbc5d5188891d8fdd805ba6c1e3b92b90092da2a837073"},
+ {file = "lazy_object_proxy-1.12.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:86fd61cb2ba249b9f436d789d1356deae69ad3231dc3c0f17293ac535162672e"},
+ {file = "lazy_object_proxy-1.12.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:81d1852fb30fab81696f93db1b1e55a5d1ff7940838191062f5f56987d5fcc3e"},
+ {file = "lazy_object_proxy-1.12.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:be9045646d83f6c2664c1330904b245ae2371b5c57a3195e4028aedc9f999655"},
+ {file = "lazy_object_proxy-1.12.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:67f07ab742f1adfb3966c40f630baaa7902be4222a17941f3d85fd1dae5565ff"},
+ {file = "lazy_object_proxy-1.12.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:75ba769017b944fcacbf6a80c18b2761a1795b03f8899acdad1f1c39db4409be"},
+ {file = "lazy_object_proxy-1.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:7b22c2bbfb155706b928ac4d74c1a63ac8552a55ba7fff4445155523ea4067e1"},
+ {file = "lazy_object_proxy-1.12.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4a79b909aa16bde8ae606f06e6bbc9d3219d2e57fb3e0076e17879072b742c65"},
+ {file = "lazy_object_proxy-1.12.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:338ab2f132276203e404951205fe80c3fd59429b3a724e7b662b2eb539bb1be9"},
+ {file = "lazy_object_proxy-1.12.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8c40b3c9faee2e32bfce0df4ae63f4e73529766893258eca78548bac801c8f66"},
+ {file = "lazy_object_proxy-1.12.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:717484c309df78cedf48396e420fa57fc8a2b1f06ea889df7248fdd156e58847"},
+ {file = "lazy_object_proxy-1.12.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a6b7ea5ea1ffe15059eb44bcbcb258f97bcb40e139b88152c40d07b1a1dfc9ac"},
+ {file = "lazy_object_proxy-1.12.0-cp313-cp313t-win_amd64.whl", hash = "sha256:08c465fb5cd23527512f9bd7b4c7ba6cec33e28aad36fbbe46bf7b858f9f3f7f"},
+ {file = "lazy_object_proxy-1.12.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c9defba70ab943f1df98a656247966d7729da2fe9c2d5d85346464bf320820a3"},
+ {file = "lazy_object_proxy-1.12.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6763941dbf97eea6b90f5b06eb4da9418cc088fce0e3883f5816090f9afcde4a"},
+ {file = "lazy_object_proxy-1.12.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fdc70d81235fc586b9e3d1aeef7d1553259b62ecaae9db2167a5d2550dcc391a"},
+ {file = "lazy_object_proxy-1.12.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:0a83c6f7a6b2bfc11ef3ed67f8cbe99f8ff500b05655d8e7df9aab993a6abc95"},
+ {file = "lazy_object_proxy-1.12.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:256262384ebd2a77b023ad02fbcc9326282bcfd16484d5531154b02bc304f4c5"},
+ {file = "lazy_object_proxy-1.12.0-cp314-cp314-win_amd64.whl", hash = "sha256:7601ec171c7e8584f8ff3f4e440aa2eebf93e854f04639263875b8c2971f819f"},
+ {file = "lazy_object_proxy-1.12.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ae575ad9b674d0029fc077c5231b3bc6b433a3d1a62a8c363df96974b5534728"},
+ {file = "lazy_object_proxy-1.12.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:31020c84005d3daa4cc0fa5a310af2066efe6b0d82aeebf9ab199292652ff036"},
+ {file = "lazy_object_proxy-1.12.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:800f32b00a47c27446a2b767df7538e6c66a3488632c402b4fb2224f9794f3c0"},
+ {file = "lazy_object_proxy-1.12.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:15400b18893f345857b9e18b9bd87bd06aba84af6ed086187add70aeaa3f93f1"},
+ {file = "lazy_object_proxy-1.12.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:3d3964fbd326578bcdfffd017ef101b6fb0484f34e731fe060ba9b8816498c36"},
+ {file = "lazy_object_proxy-1.12.0-cp39-cp39-win_amd64.whl", hash = "sha256:424a8ab6695400845c39f13c685050eab69fa0bbac5790b201cd27375e5e41d7"},
+ {file = "lazy_object_proxy-1.12.0-pp39.pp310.pp311.graalpy311-none-any.whl", hash = "sha256:c3b2e0af1f7f77c4263759c4824316ce458fabe0fceadcd24ef8ca08b2d1e402"},
+ {file = "lazy_object_proxy-1.12.0.tar.gz", hash = "sha256:1f5a462d92fd0cfb82f1fab28b51bfb209fabbe6aabf7f0d51472c0c124c0c61"},
+]
+
+[[package]]
+name = "markdown-it-py"
+version = "3.0.0"
+description = "Python port of markdown-it. Markdown parsing, done right!"
+optional = false
+python-versions = ">=3.8"
+groups = ["dev", "docs"]
+files = [
+ {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"},
+ {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"},
+]
+
+[package.dependencies]
+mdurl = ">=0.1,<1.0"
+
+[package.extras]
+benchmarking = ["psutil", "pytest", "pytest-benchmark"]
+code-style = ["pre-commit (>=3.0,<4.0)"]
+compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"]
+linkify = ["linkify-it-py (>=1,<3)"]
+plugins = ["mdit-py-plugins"]
+profiling = ["gprof2dot"]
+rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"]
+testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"]
+
+[[package]]
+name = "markupsafe"
+version = "3.0.2"
+description = "Safely add untrusted strings to HTML/XML markup."
+optional = false
+python-versions = ">=3.9"
+groups = ["main", "docs"]
+files = [
+ {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"},
+ {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"},
+ {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579"},
+ {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d"},
+ {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb"},
+ {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b"},
+ {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c"},
+ {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171"},
+ {file = "MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50"},
+ {file = "MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a"},
+ {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d"},
+ {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93"},
+ {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832"},
+ {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84"},
+ {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca"},
+ {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798"},
+ {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e"},
+ {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4"},
+ {file = "MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d"},
+ {file = "MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b"},
+ {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf"},
+ {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225"},
+ {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028"},
+ {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8"},
+ {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c"},
+ {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557"},
+ {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22"},
+ {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48"},
+ {file = "MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30"},
+ {file = "MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87"},
+ {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd"},
+ {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430"},
+ {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094"},
+ {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396"},
+ {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79"},
+ {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a"},
+ {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca"},
+ {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c"},
+ {file = "MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1"},
+ {file = "MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f"},
+ {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c"},
+ {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb"},
+ {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c"},
+ {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d"},
+ {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe"},
+ {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5"},
+ {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a"},
+ {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9"},
+ {file = "MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6"},
+ {file = "MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f"},
+ {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a"},
+ {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff"},
+ {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13"},
+ {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144"},
+ {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29"},
+ {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0"},
+ {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0"},
+ {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178"},
+ {file = "MarkupSafe-3.0.2-cp39-cp39-win32.whl", hash = "sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f"},
+ {file = "MarkupSafe-3.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a"},
+ {file = "markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0"},
+]
+
+[[package]]
+name = "matplotlib"
+version = "3.9.4"
+description = "Python plotting package"
+optional = false
+python-versions = ">=3.9"
+groups = ["main"]
+markers = "python_version < \"3.11\""
+files = [
+ {file = "matplotlib-3.9.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:c5fdd7abfb706dfa8d307af64a87f1a862879ec3cd8d0ec8637458f0885b9c50"},
+ {file = "matplotlib-3.9.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d89bc4e85e40a71d1477780366c27fb7c6494d293e1617788986f74e2a03d7ff"},
+ {file = "matplotlib-3.9.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ddf9f3c26aae695c5daafbf6b94e4c1a30d6cd617ba594bbbded3b33a1fcfa26"},
+ {file = "matplotlib-3.9.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18ebcf248030173b59a868fda1fe42397253f6698995b55e81e1f57431d85e50"},
+ {file = "matplotlib-3.9.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:974896ec43c672ec23f3f8c648981e8bc880ee163146e0312a9b8def2fac66f5"},
+ {file = "matplotlib-3.9.4-cp310-cp310-win_amd64.whl", hash = "sha256:4598c394ae9711cec135639374e70871fa36b56afae17bdf032a345be552a88d"},
+ {file = "matplotlib-3.9.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d4dd29641d9fb8bc4492420c5480398dd40a09afd73aebe4eb9d0071a05fbe0c"},
+ {file = "matplotlib-3.9.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30e5b22e8bcfb95442bf7d48b0d7f3bdf4a450cbf68986ea45fca3d11ae9d099"},
+ {file = "matplotlib-3.9.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2bb0030d1d447fd56dcc23b4c64a26e44e898f0416276cac1ebc25522e0ac249"},
+ {file = "matplotlib-3.9.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aca90ed222ac3565d2752b83dbb27627480d27662671e4d39da72e97f657a423"},
+ {file = "matplotlib-3.9.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a181b2aa2906c608fcae72f977a4a2d76e385578939891b91c2550c39ecf361e"},
+ {file = "matplotlib-3.9.4-cp311-cp311-win_amd64.whl", hash = "sha256:1f6882828231eca17f501c4dcd98a05abb3f03d157fbc0769c6911fe08b6cfd3"},
+ {file = "matplotlib-3.9.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:dfc48d67e6661378a21c2983200a654b72b5c5cdbd5d2cf6e5e1ece860f0cc70"},
+ {file = "matplotlib-3.9.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:47aef0fab8332d02d68e786eba8113ffd6f862182ea2999379dec9e237b7e483"},
+ {file = "matplotlib-3.9.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fba1f52c6b7dc764097f52fd9ab627b90db452c9feb653a59945de16752e965f"},
+ {file = "matplotlib-3.9.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:173ac3748acaac21afcc3fa1633924609ba1b87749006bc25051c52c422a5d00"},
+ {file = "matplotlib-3.9.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:320edea0cadc07007765e33f878b13b3738ffa9745c5f707705692df70ffe0e0"},
+ {file = "matplotlib-3.9.4-cp312-cp312-win_amd64.whl", hash = "sha256:a4a4cfc82330b27042a7169533da7991e8789d180dd5b3daeaee57d75cd5a03b"},
+ {file = "matplotlib-3.9.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:37eeffeeca3c940985b80f5b9a7b95ea35671e0e7405001f249848d2b62351b6"},
+ {file = "matplotlib-3.9.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3e7465ac859ee4abcb0d836137cd8414e7bb7ad330d905abced457217d4f0f45"},
+ {file = "matplotlib-3.9.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4c12302c34afa0cf061bea23b331e747e5e554b0fa595c96e01c7b75bc3b858"},
+ {file = "matplotlib-3.9.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2b8c97917f21b75e72108b97707ba3d48f171541a74aa2a56df7a40626bafc64"},
+ {file = "matplotlib-3.9.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0229803bd7e19271b03cb09f27db76c918c467aa4ce2ae168171bc67c3f508df"},
+ {file = "matplotlib-3.9.4-cp313-cp313-win_amd64.whl", hash = "sha256:7c0d8ef442ebf56ff5e206f8083d08252ee738e04f3dc88ea882853a05488799"},
+ {file = "matplotlib-3.9.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a04c3b00066a688834356d196136349cb32f5e1003c55ac419e91585168b88fb"},
+ {file = "matplotlib-3.9.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:04c519587f6c210626741a1e9a68eefc05966ede24205db8982841826af5871a"},
+ {file = "matplotlib-3.9.4-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:308afbf1a228b8b525fcd5cec17f246bbbb63b175a3ef6eb7b4d33287ca0cf0c"},
+ {file = "matplotlib-3.9.4-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ddb3b02246ddcffd3ce98e88fed5b238bc5faff10dbbaa42090ea13241d15764"},
+ {file = "matplotlib-3.9.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8a75287e9cb9eee48cb79ec1d806f75b29c0fde978cb7223a1f4c5848d696041"},
+ {file = "matplotlib-3.9.4-cp313-cp313t-win_amd64.whl", hash = "sha256:488deb7af140f0ba86da003e66e10d55ff915e152c78b4b66d231638400b1965"},
+ {file = "matplotlib-3.9.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:3c3724d89a387ddf78ff88d2a30ca78ac2b4c89cf37f2db4bd453c34799e933c"},
+ {file = "matplotlib-3.9.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d5f0a8430ffe23d7e32cfd86445864ccad141797f7d25b7c41759a5b5d17cfd7"},
+ {file = "matplotlib-3.9.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6bb0141a21aef3b64b633dc4d16cbd5fc538b727e4958be82a0e1c92a234160e"},
+ {file = "matplotlib-3.9.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:57aa235109e9eed52e2c2949db17da185383fa71083c00c6c143a60e07e0888c"},
+ {file = "matplotlib-3.9.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:b18c600061477ccfdd1e6fd050c33d8be82431700f3452b297a56d9ed7037abb"},
+ {file = "matplotlib-3.9.4-cp39-cp39-win_amd64.whl", hash = "sha256:ef5f2d1b67d2d2145ff75e10f8c008bfbf71d45137c4b648c87193e7dd053eac"},
+ {file = "matplotlib-3.9.4-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:44e0ed786d769d85bc787b0606a53f2d8d2d1d3c8a2608237365e9121c1a338c"},
+ {file = "matplotlib-3.9.4-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:09debb9ce941eb23ecdbe7eab972b1c3e0276dcf01688073faff7b0f61d6c6ca"},
+ {file = "matplotlib-3.9.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bcc53cf157a657bfd03afab14774d54ba73aa84d42cfe2480c91bd94873952db"},
+ {file = "matplotlib-3.9.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:ad45da51be7ad02387801fd154ef74d942f49fe3fcd26a64c94842ba7ec0d865"},
+ {file = "matplotlib-3.9.4.tar.gz", hash = "sha256:1e00e8be7393cbdc6fedfa8a6fba02cf3e83814b285db1c60b906a023ba41bc3"},
+]
+
+[package.dependencies]
+contourpy = ">=1.0.1"
+cycler = ">=0.10"
+fonttools = ">=4.22.0"
+importlib-resources = {version = ">=3.2.0", markers = "python_version < \"3.10\""}
+kiwisolver = ">=1.3.1"
+numpy = ">=1.23"
+packaging = ">=20.0"
+pillow = ">=8"
+pyparsing = ">=2.3.1"
+python-dateutil = ">=2.7"
+
+[package.extras]
+dev = ["meson-python (>=0.13.1,<0.17.0)", "numpy (>=1.25)", "pybind11 (>=2.6,!=2.13.3)", "setuptools (>=64)", "setuptools_scm (>=7)"]
+
+[[package]]
+name = "matplotlib"
+version = "3.10.6"
+description = "Python plotting package"
+optional = false
+python-versions = ">=3.10"
+groups = ["main"]
+markers = "python_version >= \"3.11\""
+files = [
+ {file = "matplotlib-3.10.6-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:bc7316c306d97463a9866b89d5cc217824e799fa0de346c8f68f4f3d27c8693d"},
+ {file = "matplotlib-3.10.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d00932b0d160ef03f59f9c0e16d1e3ac89646f7785165ce6ad40c842db16cc2e"},
+ {file = "matplotlib-3.10.6-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8fa4c43d6bfdbfec09c733bca8667de11bfa4970e8324c471f3a3632a0301c15"},
+ {file = "matplotlib-3.10.6-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ea117a9c1627acaa04dbf36265691921b999cbf515a015298e54e1a12c3af837"},
+ {file = "matplotlib-3.10.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:08fc803293b4e1694ee325896030de97f74c141ccff0be886bb5915269247676"},
+ {file = "matplotlib-3.10.6-cp310-cp310-win_amd64.whl", hash = "sha256:2adf92d9b7527fbfb8818e050260f0ebaa460f79d61546374ce73506c9421d09"},
+ {file = "matplotlib-3.10.6-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:905b60d1cb0ee604ce65b297b61cf8be9f4e6cfecf95a3fe1c388b5266bc8f4f"},
+ {file = "matplotlib-3.10.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7bac38d816637343e53d7185d0c66677ff30ffb131044a81898b5792c956ba76"},
+ {file = "matplotlib-3.10.6-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:942a8de2b5bfff1de31d95722f702e2966b8a7e31f4e68f7cd963c7cd8861cf6"},
+ {file = "matplotlib-3.10.6-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a3276c85370bc0dfca051ec65c5817d1e0f8f5ce1b7787528ec8ed2d524bbc2f"},
+ {file = "matplotlib-3.10.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9df5851b219225731f564e4b9e7f2ac1e13c9e6481f941b5631a0f8e2d9387ce"},
+ {file = "matplotlib-3.10.6-cp311-cp311-win_amd64.whl", hash = "sha256:abb5d9478625dd9c9eb51a06d39aae71eda749ae9b3138afb23eb38824026c7e"},
+ {file = "matplotlib-3.10.6-cp311-cp311-win_arm64.whl", hash = "sha256:886f989ccfae63659183173bb3fced7fd65e9eb793c3cc21c273add368536951"},
+ {file = "matplotlib-3.10.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:31ca662df6a80bd426f871105fdd69db7543e28e73a9f2afe80de7e531eb2347"},
+ {file = "matplotlib-3.10.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1678bb61d897bb4ac4757b5ecfb02bfb3fddf7f808000fb81e09c510712fda75"},
+ {file = "matplotlib-3.10.6-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:56cd2d20842f58c03d2d6e6c1f1cf5548ad6f66b91e1e48f814e4fb5abd1cb95"},
+ {file = "matplotlib-3.10.6-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:662df55604a2f9a45435566d6e2660e41efe83cd94f4288dfbf1e6d1eae4b0bb"},
+ {file = "matplotlib-3.10.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:08f141d55148cd1fc870c3387d70ca4df16dee10e909b3b038782bd4bda6ea07"},
+ {file = "matplotlib-3.10.6-cp312-cp312-win_amd64.whl", hash = "sha256:590f5925c2d650b5c9d813c5b3b5fc53f2929c3f8ef463e4ecfa7e052044fb2b"},
+ {file = "matplotlib-3.10.6-cp312-cp312-win_arm64.whl", hash = "sha256:f44c8d264a71609c79a78d50349e724f5d5fc3684ead7c2a473665ee63d868aa"},
+ {file = "matplotlib-3.10.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:819e409653c1106c8deaf62e6de6b8611449c2cd9939acb0d7d4e57a3d95cc7a"},
+ {file = "matplotlib-3.10.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:59c8ac8382fefb9cb71308dde16a7c487432f5255d8f1fd32473523abecfecdf"},
+ {file = "matplotlib-3.10.6-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:84e82d9e0fd70c70bc55739defbd8055c54300750cbacf4740c9673a24d6933a"},
+ {file = "matplotlib-3.10.6-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:25f7a3eb42d6c1c56e89eacd495661fc815ffc08d9da750bca766771c0fd9110"},
+ {file = "matplotlib-3.10.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f9c862d91ec0b7842920a4cfdaaec29662195301914ea54c33e01f1a28d014b2"},
+ {file = "matplotlib-3.10.6-cp313-cp313-win_amd64.whl", hash = "sha256:1b53bd6337eba483e2e7d29c5ab10eee644bc3a2491ec67cc55f7b44583ffb18"},
+ {file = "matplotlib-3.10.6-cp313-cp313-win_arm64.whl", hash = "sha256:cbd5eb50b7058b2892ce45c2f4e92557f395c9991f5c886d1bb74a1582e70fd6"},
+ {file = "matplotlib-3.10.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:acc86dd6e0e695c095001a7fccff158c49e45e0758fdf5dcdbb0103318b59c9f"},
+ {file = "matplotlib-3.10.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e228cd2ffb8f88b7d0b29e37f68ca9aaf83e33821f24a5ccc4f082dd8396bc27"},
+ {file = "matplotlib-3.10.6-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:658bc91894adeab669cf4bb4a186d049948262987e80f0857216387d7435d833"},
+ {file = "matplotlib-3.10.6-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8913b7474f6dd83ac444c9459c91f7f0f2859e839f41d642691b104e0af056aa"},
+ {file = "matplotlib-3.10.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:091cea22e059b89f6d7d1a18e2c33a7376c26eee60e401d92a4d6726c4e12706"},
+ {file = "matplotlib-3.10.6-cp313-cp313t-win_amd64.whl", hash = "sha256:491e25e02a23d7207629d942c666924a6b61e007a48177fdd231a0097b7f507e"},
+ {file = "matplotlib-3.10.6-cp313-cp313t-win_arm64.whl", hash = "sha256:3d80d60d4e54cda462e2cd9a086d85cd9f20943ead92f575ce86885a43a565d5"},
+ {file = "matplotlib-3.10.6-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:70aaf890ce1d0efd482df969b28a5b30ea0b891224bb315810a3940f67182899"},
+ {file = "matplotlib-3.10.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1565aae810ab79cb72e402b22facfa6501365e73ebab70a0fdfb98488d2c3c0c"},
+ {file = "matplotlib-3.10.6-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f3b23315a01981689aa4e1a179dbf6ef9fbd17143c3eea77548c2ecfb0499438"},
+ {file = "matplotlib-3.10.6-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:30fdd37edf41a4e6785f9b37969de57aea770696cb637d9946eb37470c94a453"},
+ {file = "matplotlib-3.10.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:bc31e693da1c08012c764b053e702c1855378e04102238e6a5ee6a7117c53a47"},
+ {file = "matplotlib-3.10.6-cp314-cp314-win_amd64.whl", hash = "sha256:05be9bdaa8b242bc6ff96330d18c52f1fc59c6fb3a4dd411d953d67e7e1baf98"},
+ {file = "matplotlib-3.10.6-cp314-cp314-win_arm64.whl", hash = "sha256:f56a0d1ab05d34c628592435781d185cd99630bdfd76822cd686fb5a0aecd43a"},
+ {file = "matplotlib-3.10.6-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:94f0b4cacb23763b64b5dace50d5b7bfe98710fed5f0cef5c08135a03399d98b"},
+ {file = "matplotlib-3.10.6-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:cc332891306b9fb39462673d8225d1b824c89783fee82840a709f96714f17a5c"},
+ {file = "matplotlib-3.10.6-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee1d607b3fb1590deb04b69f02ea1d53ed0b0bf75b2b1a5745f269afcbd3cdd3"},
+ {file = "matplotlib-3.10.6-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:376a624a218116461696b27b2bbf7a8945053e6d799f6502fc03226d077807bf"},
+ {file = "matplotlib-3.10.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:83847b47f6524c34b4f2d3ce726bb0541c48c8e7692729865c3df75bfa0f495a"},
+ {file = "matplotlib-3.10.6-cp314-cp314t-win_amd64.whl", hash = "sha256:c7e0518e0d223683532a07f4b512e2e0729b62674f1b3a1a69869f98e6b1c7e3"},
+ {file = "matplotlib-3.10.6-cp314-cp314t-win_arm64.whl", hash = "sha256:4dd83e029f5b4801eeb87c64efd80e732452781c16a9cf7415b7b63ec8f374d7"},
+ {file = "matplotlib-3.10.6-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:13fcd07ccf17e354398358e0307a1f53f5325dca22982556ddb9c52837b5af41"},
+ {file = "matplotlib-3.10.6-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:470fc846d59d1406e34fa4c32ba371039cd12c2fe86801159a965956f2575bd1"},
+ {file = "matplotlib-3.10.6-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f7173f8551b88f4ef810a94adae3128c2530e0d07529f7141be7f8d8c365f051"},
+ {file = "matplotlib-3.10.6-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:f2d684c3204fa62421bbf770ddfebc6b50130f9cad65531eeba19236d73bb488"},
+ {file = "matplotlib-3.10.6-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:6f4a69196e663a41d12a728fab8751177215357906436804217d6d9cf0d4d6cf"},
+ {file = "matplotlib-3.10.6-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d6ca6ef03dfd269f4ead566ec6f3fb9becf8dab146fb999022ed85ee9f6b3eb"},
+ {file = "matplotlib-3.10.6.tar.gz", hash = "sha256:ec01b645840dd1996df21ee37f208cd8ba57644779fa20464010638013d3203c"},
+]
+
+[package.dependencies]
+contourpy = ">=1.0.1"
+cycler = ">=0.10"
+fonttools = ">=4.22.0"
+kiwisolver = ">=1.3.1"
+numpy = ">=1.23"
+packaging = ">=20.0"
+pillow = ">=8"
+pyparsing = ">=2.3.1"
+python-dateutil = ">=2.7"
+
+[package.extras]
+dev = ["meson-python (>=0.13.1,<0.17.0)", "pybind11 (>=2.13.2,!=2.13.3)", "setuptools (>=64)", "setuptools_scm (>=7)"]
+
+[[package]]
+name = "mccabe"
+version = "0.7.0"
+description = "McCabe checker, plugin for flake8"
+optional = false
+python-versions = ">=3.6"
+groups = ["dev"]
+files = [
+ {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"},
+ {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"},
+]
+
+[[package]]
+name = "mdit-py-plugins"
+version = "0.4.2"
+description = "Collection of plugins for markdown-it-py"
+optional = false
+python-versions = ">=3.8"
+groups = ["docs"]
+markers = "python_version < \"3.11\""
+files = [
+ {file = "mdit_py_plugins-0.4.2-py3-none-any.whl", hash = "sha256:0c673c3f889399a33b95e88d2f0d111b4447bdfea7f237dab2d488f459835636"},
+ {file = "mdit_py_plugins-0.4.2.tar.gz", hash = "sha256:5f2cd1fdb606ddf152d37ec30e46101a60512bc0e5fa1a7002c36647b09e26b5"},
+]
+
+[package.dependencies]
+markdown-it-py = ">=1.0.0,<4.0.0"
+
+[package.extras]
+code-style = ["pre-commit"]
+rtd = ["myst-parser", "sphinx-book-theme"]
+testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"]
+
+[[package]]
+name = "mdit-py-plugins"
+version = "0.5.0"
+description = "Collection of plugins for markdown-it-py"
+optional = false
+python-versions = ">=3.10"
+groups = ["docs"]
+markers = "python_version >= \"3.11\""
+files = [
+ {file = "mdit_py_plugins-0.5.0-py3-none-any.whl", hash = "sha256:07a08422fc1936a5d26d146759e9155ea466e842f5ab2f7d2266dd084c8dab1f"},
+ {file = "mdit_py_plugins-0.5.0.tar.gz", hash = "sha256:f4918cb50119f50446560513a8e311d574ff6aaed72606ddae6d35716fe809c6"},
+]
+
+[package.dependencies]
+markdown-it-py = ">=2.0.0,<5.0.0"
+
+[package.extras]
+code-style = ["pre-commit"]
+rtd = ["myst-parser", "sphinx-book-theme"]
+testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"]
+
+[[package]]
+name = "mdurl"
+version = "0.1.2"
+description = "Markdown URL utilities"
+optional = false
+python-versions = ">=3.7"
+groups = ["dev", "docs"]
+files = [
+ {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"},
+ {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"},
+]
+
+[[package]]
+name = "mock"
+version = "5.2.0"
+description = "Rolling backport of unittest.mock for all Pythons"
+optional = false
+python-versions = ">=3.6"
+groups = ["dev"]
+files = [
+ {file = "mock-5.2.0-py3-none-any.whl", hash = "sha256:7ba87f72ca0e915175596069dbbcc7c75af7b5e9b9bc107ad6349ede0819982f"},
+ {file = "mock-5.2.0.tar.gz", hash = "sha256:4e460e818629b4b173f32d08bf30d3af8123afbb8e04bb5707a1fd4799e503f0"},
+]
+
+[package.extras]
+build = ["blurb", "twine", "wheel"]
+docs = ["sphinx"]
+test = ["pytest", "pytest-cov"]
+
+[[package]]
+name = "mpmath"
+version = "1.3.0"
+description = "Python library for arbitrary-precision floating-point arithmetic"
+optional = false
+python-versions = "*"
+groups = ["main"]
+files = [
+ {file = "mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c"},
+ {file = "mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f"},
+]
+
+[package.extras]
+develop = ["codecov", "pycodestyle", "pytest (>=4.6)", "pytest-cov", "wheel"]
+docs = ["sphinx"]
+gmpy = ["gmpy2 (>=2.1.0a4) ; platform_python_implementation != \"PyPy\""]
+tests = ["pytest (>=4.6)"]
+
+[[package]]
+name = "multidict"
+version = "6.6.4"
+description = "multidict implementation"
+optional = false
+python-versions = ">=3.9"
+groups = ["main"]
+files = [
+ {file = "multidict-6.6.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b8aa6f0bd8125ddd04a6593437bad6a7e70f300ff4180a531654aa2ab3f6d58f"},
+ {file = "multidict-6.6.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b9e5853bbd7264baca42ffc53391b490d65fe62849bf2c690fa3f6273dbcd0cb"},
+ {file = "multidict-6.6.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0af5f9dee472371e36d6ae38bde009bd8ce65ac7335f55dcc240379d7bed1495"},
+ {file = "multidict-6.6.4-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:d24f351e4d759f5054b641c81e8291e5d122af0fca5c72454ff77f7cbe492de8"},
+ {file = "multidict-6.6.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:db6a3810eec08280a172a6cd541ff4a5f6a97b161d93ec94e6c4018917deb6b7"},
+ {file = "multidict-6.6.4-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a1b20a9d56b2d81e2ff52ecc0670d583eaabaa55f402e8d16dd062373dbbe796"},
+ {file = "multidict-6.6.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8c9854df0eaa610a23494c32a6f44a3a550fb398b6b51a56e8c6b9b3689578db"},
+ {file = "multidict-6.6.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4bb7627fd7a968f41905a4d6343b0d63244a0623f006e9ed989fa2b78f4438a0"},
+ {file = "multidict-6.6.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:caebafea30ed049c57c673d0b36238b1748683be2593965614d7b0e99125c877"},
+ {file = "multidict-6.6.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ad887a8250eb47d3ab083d2f98db7f48098d13d42eb7a3b67d8a5c795f224ace"},
+ {file = "multidict-6.6.4-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:ed8358ae7d94ffb7c397cecb62cbac9578a83ecefc1eba27b9090ee910e2efb6"},
+ {file = "multidict-6.6.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:ecab51ad2462197a4c000b6d5701fc8585b80eecb90583635d7e327b7b6923eb"},
+ {file = "multidict-6.6.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:c5c97aa666cf70e667dfa5af945424ba1329af5dd988a437efeb3a09430389fb"},
+ {file = "multidict-6.6.4-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:9a950b7cf54099c1209f455ac5970b1ea81410f2af60ed9eb3c3f14f0bfcf987"},
+ {file = "multidict-6.6.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:163c7ea522ea9365a8a57832dea7618e6cbdc3cd75f8c627663587459a4e328f"},
+ {file = "multidict-6.6.4-cp310-cp310-win32.whl", hash = "sha256:17d2cbbfa6ff20821396b25890f155f40c986f9cfbce5667759696d83504954f"},
+ {file = "multidict-6.6.4-cp310-cp310-win_amd64.whl", hash = "sha256:ce9a40fbe52e57e7edf20113a4eaddfacac0561a0879734e636aa6d4bb5e3fb0"},
+ {file = "multidict-6.6.4-cp310-cp310-win_arm64.whl", hash = "sha256:01d0959807a451fe9fdd4da3e139cb5b77f7328baf2140feeaf233e1d777b729"},
+ {file = "multidict-6.6.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c7a0e9b561e6460484318a7612e725df1145d46b0ef57c6b9866441bf6e27e0c"},
+ {file = "multidict-6.6.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6bf2f10f70acc7a2446965ffbc726e5fc0b272c97a90b485857e5c70022213eb"},
+ {file = "multidict-6.6.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:66247d72ed62d5dd29752ffc1d3b88f135c6a8de8b5f63b7c14e973ef5bda19e"},
+ {file = "multidict-6.6.4-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:105245cc6b76f51e408451a844a54e6823bbd5a490ebfe5bdfc79798511ceded"},
+ {file = "multidict-6.6.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cbbc54e58b34c3bae389ef00046be0961f30fef7cb0dd9c7756aee376a4f7683"},
+ {file = "multidict-6.6.4-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:56c6b3652f945c9bc3ac6c8178cd93132b8d82dd581fcbc3a00676c51302bc1a"},
+ {file = "multidict-6.6.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b95494daf857602eccf4c18ca33337dd2be705bccdb6dddbfc9d513e6addb9d9"},
+ {file = "multidict-6.6.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e5b1413361cef15340ab9dc61523e653d25723e82d488ef7d60a12878227ed50"},
+ {file = "multidict-6.6.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e167bf899c3d724f9662ef00b4f7fef87a19c22b2fead198a6f68b263618df52"},
+ {file = "multidict-6.6.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:aaea28ba20a9026dfa77f4b80369e51cb767c61e33a2d4043399c67bd95fb7c6"},
+ {file = "multidict-6.6.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:8c91cdb30809a96d9ecf442ec9bc45e8cfaa0f7f8bdf534e082c2443a196727e"},
+ {file = "multidict-6.6.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1a0ccbfe93ca114c5d65a2471d52d8829e56d467c97b0e341cf5ee45410033b3"},
+ {file = "multidict-6.6.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:55624b3f321d84c403cb7d8e6e982f41ae233d85f85db54ba6286f7295dc8a9c"},
+ {file = "multidict-6.6.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:4a1fb393a2c9d202cb766c76208bd7945bc194eba8ac920ce98c6e458f0b524b"},
+ {file = "multidict-6.6.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:43868297a5759a845fa3a483fb4392973a95fb1de891605a3728130c52b8f40f"},
+ {file = "multidict-6.6.4-cp311-cp311-win32.whl", hash = "sha256:ed3b94c5e362a8a84d69642dbeac615452e8af9b8eb825b7bc9f31a53a1051e2"},
+ {file = "multidict-6.6.4-cp311-cp311-win_amd64.whl", hash = "sha256:d8c112f7a90d8ca5d20213aa41eac690bb50a76da153e3afb3886418e61cb22e"},
+ {file = "multidict-6.6.4-cp311-cp311-win_arm64.whl", hash = "sha256:3bb0eae408fa1996d87247ca0d6a57b7fc1dcf83e8a5c47ab82c558c250d4adf"},
+ {file = "multidict-6.6.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0ffb87be160942d56d7b87b0fdf098e81ed565add09eaa1294268c7f3caac4c8"},
+ {file = "multidict-6.6.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d191de6cbab2aff5de6c5723101705fd044b3e4c7cfd587a1929b5028b9714b3"},
+ {file = "multidict-6.6.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:38a0956dd92d918ad5feff3db8fcb4a5eb7dba114da917e1a88475619781b57b"},
+ {file = "multidict-6.6.4-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:6865f6d3b7900ae020b495d599fcf3765653bc927951c1abb959017f81ae8287"},
+ {file = "multidict-6.6.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a2088c126b6f72db6c9212ad827d0ba088c01d951cee25e758c450da732c138"},
+ {file = "multidict-6.6.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0f37bed7319b848097085d7d48116f545985db988e2256b2e6f00563a3416ee6"},
+ {file = "multidict-6.6.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:01368e3c94032ba6ca0b78e7ccb099643466cf24f8dc8eefcfdc0571d56e58f9"},
+ {file = "multidict-6.6.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8fe323540c255db0bffee79ad7f048c909f2ab0edb87a597e1c17da6a54e493c"},
+ {file = "multidict-6.6.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8eb3025f17b0a4c3cd08cda49acf312a19ad6e8a4edd9dbd591e6506d999402"},
+ {file = "multidict-6.6.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bbc14f0365534d35a06970d6a83478b249752e922d662dc24d489af1aa0d1be7"},
+ {file = "multidict-6.6.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:75aa52fba2d96bf972e85451b99d8e19cc37ce26fd016f6d4aa60da9ab2b005f"},
+ {file = "multidict-6.6.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4fefd4a815e362d4f011919d97d7b4a1e566f1dde83dc4ad8cfb5b41de1df68d"},
+ {file = "multidict-6.6.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:db9801fe021f59a5b375ab778973127ca0ac52429a26e2fd86aa9508f4d26eb7"},
+ {file = "multidict-6.6.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:a650629970fa21ac1fb06ba25dabfc5b8a2054fcbf6ae97c758aa956b8dba802"},
+ {file = "multidict-6.6.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:452ff5da78d4720d7516a3a2abd804957532dd69296cb77319c193e3ffb87e24"},
+ {file = "multidict-6.6.4-cp312-cp312-win32.whl", hash = "sha256:8c2fcb12136530ed19572bbba61b407f655e3953ba669b96a35036a11a485793"},
+ {file = "multidict-6.6.4-cp312-cp312-win_amd64.whl", hash = "sha256:047d9425860a8c9544fed1b9584f0c8bcd31bcde9568b047c5e567a1025ecd6e"},
+ {file = "multidict-6.6.4-cp312-cp312-win_arm64.whl", hash = "sha256:14754eb72feaa1e8ae528468f24250dd997b8e2188c3d2f593f9eba259e4b364"},
+ {file = "multidict-6.6.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f46a6e8597f9bd71b31cc708195d42b634c8527fecbcf93febf1052cacc1f16e"},
+ {file = "multidict-6.6.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:22e38b2bc176c5eb9c0a0e379f9d188ae4cd8b28c0f53b52bce7ab0a9e534657"},
+ {file = "multidict-6.6.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5df8afd26f162da59e218ac0eefaa01b01b2e6cd606cffa46608f699539246da"},
+ {file = "multidict-6.6.4-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:49517449b58d043023720aa58e62b2f74ce9b28f740a0b5d33971149553d72aa"},
+ {file = "multidict-6.6.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ae9408439537c5afdca05edd128a63f56a62680f4b3c234301055d7a2000220f"},
+ {file = "multidict-6.6.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:87a32d20759dc52a9e850fe1061b6e41ab28e2998d44168a8a341b99ded1dba0"},
+ {file = "multidict-6.6.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:52e3c8d43cdfff587ceedce9deb25e6ae77daba560b626e97a56ddcad3756879"},
+ {file = "multidict-6.6.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ad8850921d3a8d8ff6fbef790e773cecfc260bbfa0566998980d3fa8f520bc4a"},
+ {file = "multidict-6.6.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:497a2954adc25c08daff36f795077f63ad33e13f19bfff7736e72c785391534f"},
+ {file = "multidict-6.6.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:024ce601f92d780ca1617ad4be5ac15b501cc2414970ffa2bb2bbc2bd5a68fa5"},
+ {file = "multidict-6.6.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:a693fc5ed9bdd1c9e898013e0da4dcc640de7963a371c0bd458e50e046bf6438"},
+ {file = "multidict-6.6.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:190766dac95aab54cae5b152a56520fd99298f32a1266d66d27fdd1b5ac00f4e"},
+ {file = "multidict-6.6.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:34d8f2a5ffdceab9dcd97c7a016deb2308531d5f0fced2bb0c9e1df45b3363d7"},
+ {file = "multidict-6.6.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:59e8d40ab1f5a8597abcef00d04845155a5693b5da00d2c93dbe88f2050f2812"},
+ {file = "multidict-6.6.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:467fe64138cfac771f0e949b938c2e1ada2b5af22f39692aa9258715e9ea613a"},
+ {file = "multidict-6.6.4-cp313-cp313-win32.whl", hash = "sha256:14616a30fe6d0a48d0a48d1a633ab3b8bec4cf293aac65f32ed116f620adfd69"},
+ {file = "multidict-6.6.4-cp313-cp313-win_amd64.whl", hash = "sha256:40cd05eaeb39e2bc8939451f033e57feaa2ac99e07dbca8afe2be450a4a3b6cf"},
+ {file = "multidict-6.6.4-cp313-cp313-win_arm64.whl", hash = "sha256:f6eb37d511bfae9e13e82cb4d1af36b91150466f24d9b2b8a9785816deb16605"},
+ {file = "multidict-6.6.4-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:6c84378acd4f37d1b507dfa0d459b449e2321b3ba5f2338f9b085cf7a7ba95eb"},
+ {file = "multidict-6.6.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0e0558693063c75f3d952abf645c78f3c5dfdd825a41d8c4d8156fc0b0da6e7e"},
+ {file = "multidict-6.6.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3f8e2384cb83ebd23fd07e9eada8ba64afc4c759cd94817433ab8c81ee4b403f"},
+ {file = "multidict-6.6.4-cp313-cp313t-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:f996b87b420995a9174b2a7c1a8daf7db4750be6848b03eb5e639674f7963773"},
+ {file = "multidict-6.6.4-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cc356250cffd6e78416cf5b40dc6a74f1edf3be8e834cf8862d9ed5265cf9b0e"},
+ {file = "multidict-6.6.4-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:dadf95aa862714ea468a49ad1e09fe00fcc9ec67d122f6596a8d40caf6cec7d0"},
+ {file = "multidict-6.6.4-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7dd57515bebffd8ebd714d101d4c434063322e4fe24042e90ced41f18b6d3395"},
+ {file = "multidict-6.6.4-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:967af5f238ebc2eb1da4e77af5492219fbd9b4b812347da39a7b5f5c72c0fa45"},
+ {file = "multidict-6.6.4-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2a4c6875c37aae9794308ec43e3530e4aa0d36579ce38d89979bbf89582002bb"},
+ {file = "multidict-6.6.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:7f683a551e92bdb7fac545b9c6f9fa2aebdeefa61d607510b3533286fcab67f5"},
+ {file = "multidict-6.6.4-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:3ba5aaf600edaf2a868a391779f7a85d93bed147854925f34edd24cc70a3e141"},
+ {file = "multidict-6.6.4-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:580b643b7fd2c295d83cad90d78419081f53fd532d1f1eb67ceb7060f61cff0d"},
+ {file = "multidict-6.6.4-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:37b7187197da6af3ee0b044dbc9625afd0c885f2800815b228a0e70f9a7f473d"},
+ {file = "multidict-6.6.4-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e1b93790ed0bc26feb72e2f08299691ceb6da5e9e14a0d13cc74f1869af327a0"},
+ {file = "multidict-6.6.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a506a77ddee1efcca81ecbeae27ade3e09cdf21a8ae854d766c2bb4f14053f92"},
+ {file = "multidict-6.6.4-cp313-cp313t-win32.whl", hash = "sha256:f93b2b2279883d1d0a9e1bd01f312d6fc315c5e4c1f09e112e4736e2f650bc4e"},
+ {file = "multidict-6.6.4-cp313-cp313t-win_amd64.whl", hash = "sha256:6d46a180acdf6e87cc41dc15d8f5c2986e1e8739dc25dbb7dac826731ef381a4"},
+ {file = "multidict-6.6.4-cp313-cp313t-win_arm64.whl", hash = "sha256:756989334015e3335d087a27331659820d53ba432befdef6a718398b0a8493ad"},
+ {file = "multidict-6.6.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:af7618b591bae552b40dbb6f93f5518328a949dac626ee75927bba1ecdeea9f4"},
+ {file = "multidict-6.6.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b6819f83aef06f560cb15482d619d0e623ce9bf155115150a85ab11b8342a665"},
+ {file = "multidict-6.6.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4d09384e75788861e046330308e7af54dd306aaf20eb760eb1d0de26b2bea2cb"},
+ {file = "multidict-6.6.4-cp39-cp39-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:a59c63061f1a07b861c004e53869eb1211ffd1a4acbca330e3322efa6dd02978"},
+ {file = "multidict-6.6.4-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:350f6b0fe1ced61e778037fdc7613f4051c8baf64b1ee19371b42a3acdb016a0"},
+ {file = "multidict-6.6.4-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0c5cbac6b55ad69cb6aa17ee9343dfbba903118fd530348c330211dc7aa756d1"},
+ {file = "multidict-6.6.4-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:630f70c32b8066ddfd920350bc236225814ad94dfa493fe1910ee17fe4365cbb"},
+ {file = "multidict-6.6.4-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f8d4916a81697faec6cb724a273bd5457e4c6c43d82b29f9dc02c5542fd21fc9"},
+ {file = "multidict-6.6.4-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8e42332cf8276bb7645d310cdecca93a16920256a5b01bebf747365f86a1675b"},
+ {file = "multidict-6.6.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:f3be27440f7644ab9a13a6fc86f09cdd90b347c3c5e30c6d6d860de822d7cb53"},
+ {file = "multidict-6.6.4-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:21f216669109e02ef3e2415ede07f4f8987f00de8cdfa0cc0b3440d42534f9f0"},
+ {file = "multidict-6.6.4-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:d9890d68c45d1aeac5178ded1d1cccf3bc8d7accf1f976f79bf63099fb16e4bd"},
+ {file = "multidict-6.6.4-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:edfdcae97cdc5d1a89477c436b61f472c4d40971774ac4729c613b4b133163cb"},
+ {file = "multidict-6.6.4-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:0b2e886624be5773e69cf32bcb8534aecdeb38943520b240fed3d5596a430f2f"},
+ {file = "multidict-6.6.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:be5bf4b3224948032a845d12ab0f69f208293742df96dc14c4ff9b09e508fc17"},
+ {file = "multidict-6.6.4-cp39-cp39-win32.whl", hash = "sha256:10a68a9191f284fe9d501fef4efe93226e74df92ce7a24e301371293bd4918ae"},
+ {file = "multidict-6.6.4-cp39-cp39-win_amd64.whl", hash = "sha256:ee25f82f53262f9ac93bd7e58e47ea1bdcc3393cef815847e397cba17e284210"},
+ {file = "multidict-6.6.4-cp39-cp39-win_arm64.whl", hash = "sha256:f9867e55590e0855bcec60d4f9a092b69476db64573c9fe17e92b0c50614c16a"},
+ {file = "multidict-6.6.4-py3-none-any.whl", hash = "sha256:27d8f8e125c07cb954e54d75d04905a9bba8a439c1d84aca94949d4d03d8601c"},
+ {file = "multidict-6.6.4.tar.gz", hash = "sha256:d2d4e4787672911b48350df02ed3fa3fffdc2f2e8ca06dd6afdf34189b76a9dd"},
+]
+
+[package.dependencies]
+typing-extensions = {version = ">=4.1.0", markers = "python_version < \"3.11\""}
+
+[[package]]
+name = "multiprocess"
+version = "0.70.16"
+description = "better multiprocessing and multithreading in Python"
+optional = false
+python-versions = ">=3.8"
+groups = ["main"]
+files = [
+ {file = "multiprocess-0.70.16-pp310-pypy310_pp73-macosx_10_13_x86_64.whl", hash = "sha256:476887be10e2f59ff183c006af746cb6f1fd0eadcfd4ef49e605cbe2659920ee"},
+ {file = "multiprocess-0.70.16-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:d951bed82c8f73929ac82c61f01a7b5ce8f3e5ef40f5b52553b4f547ce2b08ec"},
+ {file = "multiprocess-0.70.16-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:37b55f71c07e2d741374998c043b9520b626a8dddc8b3129222ca4f1a06ef67a"},
+ {file = "multiprocess-0.70.16-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:ba8c31889abf4511c7308a8c52bb4a30b9d590e7f58523302ba00237702ca054"},
+ {file = "multiprocess-0.70.16-pp39-pypy39_pp73-macosx_10_13_x86_64.whl", hash = "sha256:0dfd078c306e08d46d7a8d06fb120313d87aa43af60d66da43ffff40b44d2f41"},
+ {file = "multiprocess-0.70.16-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e7b9d0f307cd9bd50851afaac0dba2cb6c44449efff697df7c7645f7d3f2be3a"},
+ {file = "multiprocess-0.70.16-py310-none-any.whl", hash = "sha256:c4a9944c67bd49f823687463660a2d6daae94c289adff97e0f9d696ba6371d02"},
+ {file = "multiprocess-0.70.16-py311-none-any.whl", hash = "sha256:af4cabb0dac72abfb1e794fa7855c325fd2b55a10a44628a3c1ad3311c04127a"},
+ {file = "multiprocess-0.70.16-py312-none-any.whl", hash = "sha256:fc0544c531920dde3b00c29863377f87e1632601092ea2daca74e4beb40faa2e"},
+ {file = "multiprocess-0.70.16-py38-none-any.whl", hash = "sha256:a71d82033454891091a226dfc319d0cfa8019a4e888ef9ca910372a446de4435"},
+ {file = "multiprocess-0.70.16-py39-none-any.whl", hash = "sha256:a0bafd3ae1b732eac64be2e72038231c1ba97724b60b09400d68f229fcc2fbf3"},
+ {file = "multiprocess-0.70.16.tar.gz", hash = "sha256:161af703d4652a0e1410be6abccecde4a7ddffd19341be0a7011b94aeb171ac1"},
+]
+
+[package.dependencies]
+dill = ">=0.3.8"
+
+[[package]]
+name = "mypy"
+version = "1.4.0"
+description = "Optional static typing for Python"
+optional = false
+python-versions = ">=3.7"
+groups = ["dev"]
+files = [
+ {file = "mypy-1.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a3af348e0925a59213244f28c7c0c3a2c2088b4ba2fe9d6c8d4fbb0aba0b7d05"},
+ {file = "mypy-1.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a0b2e0da7ff9dd8d2066d093d35a169305fc4e38db378281fce096768a3dbdbf"},
+ {file = "mypy-1.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:210fe0f39ec5be45dd9d0de253cb79245f0a6f27631d62e0c9c7988be7152965"},
+ {file = "mypy-1.4.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f7a5971490fd4a5a436e143105a1f78fa8b3fe95b30fff2a77542b4f3227a01f"},
+ {file = "mypy-1.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:50f65f0e9985f1e50040e603baebab83efed9eb37e15a22a4246fa7cd660f981"},
+ {file = "mypy-1.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3b1b5c875fcf3e7217a3de7f708166f641ca154b589664c44a6fd6d9f17d9e7e"},
+ {file = "mypy-1.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b4c734d947e761c7ceb1f09a98359dd5666460acbc39f7d0a6b6beec373c5840"},
+ {file = "mypy-1.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5984a8d13d35624e3b235a793c814433d810acba9eeefe665cdfed3d08bc3af"},
+ {file = "mypy-1.4.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0f98973e39e4a98709546a9afd82e1ffcc50c6ec9ce6f7870f33ebbf0bd4f26d"},
+ {file = "mypy-1.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:19d42b08c7532d736a7e0fb29525855e355fa51fd6aef4f9bbc80749ff64b1a2"},
+ {file = "mypy-1.4.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6ba9a69172abaa73910643744d3848877d6aac4a20c41742027dcfd8d78f05d9"},
+ {file = "mypy-1.4.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a34eed094c16cad0f6b0d889811592c7a9b7acf10d10a7356349e325d8704b4f"},
+ {file = "mypy-1.4.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:53c2a1fed81e05ded10a4557fe12bae05b9ecf9153f162c662a71d924d504135"},
+ {file = "mypy-1.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:bba57b4d2328740749f676807fcf3036e9de723530781405cc5a5e41fc6e20de"},
+ {file = "mypy-1.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:653863c75f0dbb687d92eb0d4bd9fe7047d096987ecac93bb7b1bc336de48ebd"},
+ {file = "mypy-1.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7461469e163f87a087a5e7aa224102a30f037c11a096a0ceeb721cb0dce274c8"},
+ {file = "mypy-1.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0cf0ca95e4b8adeaf07815a78b4096b65adf64ea7871b39a2116c19497fcd0dd"},
+ {file = "mypy-1.4.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:94a81b9354545123feb1a99b960faeff9e1fa204fce47e0042335b473d71530d"},
+ {file = "mypy-1.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:67242d5b28ed0fa88edd8f880aed24da481929467fdbca6487167cb5e3fd31ff"},
+ {file = "mypy-1.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3f2b353eebef669529d9bd5ae3566905a685ae98b3af3aad7476d0d519714758"},
+ {file = "mypy-1.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:62bf18d97c6b089f77f0067b4e321db089d8520cdeefc6ae3ec0f873621c22e5"},
+ {file = "mypy-1.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca33ab70a4aaa75bb01086a0b04f0ba8441e51e06fc57e28585176b08cad533b"},
+ {file = "mypy-1.4.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5a0ee54c2cb0f957f8a6f41794d68f1a7e32b9968675ade5846f538504856d42"},
+ {file = "mypy-1.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:6c34d43e3d54ad05024576aef28081d9d0580f6fa7f131255f54020eb12f5352"},
+ {file = "mypy-1.4.0-py3-none-any.whl", hash = "sha256:f051ca656be0c179c735a4c3193f307d34c92fdc4908d44fd4516fbe8b10567d"},
+ {file = "mypy-1.4.0.tar.gz", hash = "sha256:de1e7e68148a213036276d1f5303b3836ad9a774188961eb2684eddff593b042"},
+]
+
+[package.dependencies]
+mypy-extensions = ">=1.0.0"
+tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
+typing-extensions = ">=3.10"
+
+[package.extras]
+dmypy = ["psutil (>=4.0)"]
+install-types = ["pip"]
+python2 = ["typed-ast (>=1.4.0,<2)"]
+reports = ["lxml"]
+
+[[package]]
+name = "mypy-extensions"
+version = "1.1.0"
+description = "Type system extensions for programs checked with the mypy type checker."
+optional = false
+python-versions = ">=3.8"
+groups = ["dev"]
+files = [
+ {file = "mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505"},
+ {file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"},
+]
+
+[[package]]
+name = "myst-parser"
+version = "3.0.1"
+description = "An extended [CommonMark](https://spec.commonmark.org/) compliant parser,"
+optional = false
+python-versions = ">=3.8"
+groups = ["docs"]
+markers = "python_version < \"3.11\""
+files = [
+ {file = "myst_parser-3.0.1-py3-none-any.whl", hash = "sha256:6457aaa33a5d474aca678b8ead9b3dc298e89c68e67012e73146ea6fd54babf1"},
+ {file = "myst_parser-3.0.1.tar.gz", hash = "sha256:88f0cb406cb363b077d176b51c476f62d60604d68a8dcdf4832e080441301a87"},
+]
+
+[package.dependencies]
+docutils = ">=0.18,<0.22"
+jinja2 = "*"
+markdown-it-py = ">=3.0,<4.0"
+mdit-py-plugins = ">=0.4,<1.0"
+pyyaml = "*"
+sphinx = ">=6,<8"
+
+[package.extras]
+code-style = ["pre-commit (>=3.0,<4.0)"]
+linkify = ["linkify-it-py (>=2.0,<3.0)"]
+rtd = ["ipython", "sphinx (>=7)", "sphinx-autodoc2 (>=0.5.0,<0.6.0)", "sphinx-book-theme (>=1.1,<2.0)", "sphinx-copybutton", "sphinx-design", "sphinx-pyscript", "sphinx-tippy (>=0.4.3)", "sphinx-togglebutton", "sphinxext-opengraph (>=0.9.0,<0.10.0)", "sphinxext-rediraffe (>=0.2.7,<0.3.0)"]
+testing = ["beautifulsoup4", "coverage[toml]", "defusedxml", "pytest (>=8,<9)", "pytest-cov", "pytest-param-files (>=0.6.0,<0.7.0)", "pytest-regressions", "sphinx-pytest"]
+testing-docutils = ["pygments", "pytest (>=8,<9)", "pytest-param-files (>=0.6.0,<0.7.0)"]
+
+[[package]]
+name = "myst-parser"
+version = "4.0.1"
+description = "An extended [CommonMark](https://spec.commonmark.org/) compliant parser,"
+optional = false
+python-versions = ">=3.10"
+groups = ["docs"]
+markers = "python_version >= \"3.11\""
+files = [
+ {file = "myst_parser-4.0.1-py3-none-any.whl", hash = "sha256:9134e88959ec3b5780aedf8a99680ea242869d012e8821db3126d427edc9c95d"},
+ {file = "myst_parser-4.0.1.tar.gz", hash = "sha256:5cfea715e4f3574138aecbf7d54132296bfd72bb614d31168f48c477a830a7c4"},
+]
+
+[package.dependencies]
+docutils = ">=0.19,<0.22"
+jinja2 = "*"
+markdown-it-py = ">=3.0,<4.0"
+mdit-py-plugins = ">=0.4.1,<1.0"
+pyyaml = "*"
+sphinx = ">=7,<9"
+
+[package.extras]
+code-style = ["pre-commit (>=4.0,<5.0)"]
+linkify = ["linkify-it-py (>=2.0,<3.0)"]
+rtd = ["ipython", "sphinx (>=7)", "sphinx-autodoc2 (>=0.5.0,<0.6.0)", "sphinx-book-theme (>=1.1,<2.0)", "sphinx-copybutton", "sphinx-design", "sphinx-pyscript", "sphinx-tippy (>=0.4.3)", "sphinx-togglebutton", "sphinxext-opengraph (>=0.9.0,<0.10.0)", "sphinxext-rediraffe (>=0.2.7,<0.3.0)"]
+testing = ["beautifulsoup4", "coverage[toml]", "defusedxml", "pygments (<2.19)", "pytest (>=8,<9)", "pytest-cov", "pytest-param-files (>=0.6.0,<0.7.0)", "pytest-regressions", "sphinx-pytest"]
+testing-docutils = ["pygments", "pytest (>=8,<9)", "pytest-param-files (>=0.6.0,<0.7.0)"]
+
+[[package]]
+name = "narwhals"
+version = "2.3.0"
+description = "Extremely lightweight compatibility layer between dataframe libraries"
+optional = false
+python-versions = ">=3.9"
+groups = ["main"]
+files = [
+ {file = "narwhals-2.3.0-py3-none-any.whl", hash = "sha256:5507b1a9a9c2b1c55a627fdf6cf722fef2e23498bd14362a332c8848a311c321"},
+ {file = "narwhals-2.3.0.tar.gz", hash = "sha256:b66bc4ab7b6746354f60c4b3941e3ce60c066588c35360e2dc6c063489000a16"},
+]
+
+[package.extras]
+cudf = ["cudf (>=24.10.0)"]
+dask = ["dask[dataframe] (>=2024.8)"]
+duckdb = ["duckdb (>=1.0)"]
+ibis = ["ibis-framework (>=6.0.0)", "packaging", "pyarrow-hotfix", "rich"]
+modin = ["modin"]
+pandas = ["pandas (>=1.1.3)"]
+polars = ["polars (>=0.20.4)"]
+pyarrow = ["pyarrow (>=13.0.0)"]
+pyspark = ["pyspark (>=3.5.0)"]
+pyspark-connect = ["pyspark[connect] (>=3.5.0)"]
+sqlframe = ["sqlframe (>=3.22.0,!=3.39.3)"]
+
+[[package]]
+name = "networkx"
+version = "3.2.1"
+description = "Python package for creating and manipulating graphs and networks"
+optional = false
+python-versions = ">=3.9"
+groups = ["main"]
+markers = "python_version < \"3.11\""
+files = [
+ {file = "networkx-3.2.1-py3-none-any.whl", hash = "sha256:f18c69adc97877c42332c170849c96cefa91881c99a7cb3e95b7c659ebdc1ec2"},
+ {file = "networkx-3.2.1.tar.gz", hash = "sha256:9f1bb5cf3409bf324e0a722c20bdb4c20ee39bf1c30ce8ae499c8502b0b5e0c6"},
+]
+
+[package.extras]
+default = ["matplotlib (>=3.5)", "numpy (>=1.22)", "pandas (>=1.4)", "scipy (>=1.9,!=1.11.0,!=1.11.1)"]
+developer = ["changelist (==0.4)", "mypy (>=1.1)", "pre-commit (>=3.2)", "rtoml"]
+doc = ["nb2plots (>=0.7)", "nbconvert (<7.9)", "numpydoc (>=1.6)", "pillow (>=9.4)", "pydata-sphinx-theme (>=0.14)", "sphinx (>=7)", "sphinx-gallery (>=0.14)", "texext (>=0.6.7)"]
+extra = ["lxml (>=4.6)", "pydot (>=1.4.2)", "pygraphviz (>=1.11)", "sympy (>=1.10)"]
+test = ["pytest (>=7.2)", "pytest-cov (>=4.0)"]
+
+[[package]]
+name = "networkx"
+version = "3.5"
+description = "Python package for creating and manipulating graphs and networks"
+optional = false
+python-versions = ">=3.11"
+groups = ["main"]
+markers = "python_version >= \"3.11\""
+files = [
+ {file = "networkx-3.5-py3-none-any.whl", hash = "sha256:0030d386a9a06dee3565298b4a734b68589749a544acbb6c412dc9e2489ec6ec"},
+ {file = "networkx-3.5.tar.gz", hash = "sha256:d4c6f9cf81f52d69230866796b82afbccdec3db7ae4fbd1b65ea750feed50037"},
+]
+
+[package.extras]
+default = ["matplotlib (>=3.8)", "numpy (>=1.25)", "pandas (>=2.0)", "scipy (>=1.11.2)"]
+developer = ["mypy (>=1.15)", "pre-commit (>=4.1)"]
+doc = ["intersphinx-registry", "myst-nb (>=1.1)", "numpydoc (>=1.8.0)", "pillow (>=10)", "pydata-sphinx-theme (>=0.16)", "sphinx (>=8.0)", "sphinx-gallery (>=0.18)", "texext (>=0.6.7)"]
+example = ["cairocffi (>=1.7)", "contextily (>=1.6)", "igraph (>=0.11)", "momepy (>=0.7.2)", "osmnx (>=2.0.0)", "scikit-learn (>=1.5)", "seaborn (>=0.13)"]
+extra = ["lxml (>=4.6)", "pydot (>=3.0.1)", "pygraphviz (>=1.14)", "sympy (>=1.10)"]
+test = ["pytest (>=7.2)", "pytest-cov (>=4.0)", "pytest-xdist (>=3.0)"]
+test-extras = ["pytest-mpl", "pytest-randomly"]
+
+[[package]]
+name = "nltk"
+version = "3.9.1"
+description = "Natural Language Toolkit"
+optional = false
+python-versions = ">=3.8"
+groups = ["main"]
+files = [
+ {file = "nltk-3.9.1-py3-none-any.whl", hash = "sha256:4fa26829c5b00715afe3061398a8989dc643b92ce7dd93fb4585a70930d168a1"},
+ {file = "nltk-3.9.1.tar.gz", hash = "sha256:87d127bd3de4bd89a4f81265e5fa59cb1b199b27440175370f7417d2bc7ae868"},
+]
+
+[package.dependencies]
+click = "*"
+joblib = "*"
+regex = ">=2021.8.3"
+tqdm = "*"
+
+[package.extras]
+all = ["matplotlib", "numpy", "pyparsing", "python-crfsuite", "requests", "scikit-learn", "scipy", "twython"]
+corenlp = ["requests"]
+machine-learning = ["numpy", "python-crfsuite", "scikit-learn", "scipy"]
+plot = ["matplotlib"]
+tgrep = ["pyparsing"]
+twitter = ["twython"]
+
+[[package]]
+name = "nodeenv"
+version = "1.9.1"
+description = "Node.js virtual environment builder"
+optional = false
+python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
+groups = ["dev"]
+files = [
+ {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"},
+ {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"},
+]
+
+[[package]]
+name = "numpy"
+version = "2.0.2"
+description = "Fundamental package for array computing in Python"
+optional = false
+python-versions = ">=3.9"
+groups = ["main"]
+markers = "python_version < \"3.11\""
+files = [
+ {file = "numpy-2.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:51129a29dbe56f9ca83438b706e2e69a39892b5eda6cedcb6b0c9fdc9b0d3ece"},
+ {file = "numpy-2.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f15975dfec0cf2239224d80e32c3170b1d168335eaedee69da84fbe9f1f9cd04"},
+ {file = "numpy-2.0.2-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:8c5713284ce4e282544c68d1c3b2c7161d38c256d2eefc93c1d683cf47683e66"},
+ {file = "numpy-2.0.2-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:becfae3ddd30736fe1889a37f1f580e245ba79a5855bff5f2a29cb3ccc22dd7b"},
+ {file = "numpy-2.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2da5960c3cf0df7eafefd806d4e612c5e19358de82cb3c343631188991566ccd"},
+ {file = "numpy-2.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:496f71341824ed9f3d2fd36cf3ac57ae2e0165c143b55c3a035ee219413f3318"},
+ {file = "numpy-2.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a61ec659f68ae254e4d237816e33171497e978140353c0c2038d46e63282d0c8"},
+ {file = "numpy-2.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d731a1c6116ba289c1e9ee714b08a8ff882944d4ad631fd411106a30f083c326"},
+ {file = "numpy-2.0.2-cp310-cp310-win32.whl", hash = "sha256:984d96121c9f9616cd33fbd0618b7f08e0cfc9600a7ee1d6fd9b239186d19d97"},
+ {file = "numpy-2.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:c7b0be4ef08607dd04da4092faee0b86607f111d5ae68036f16cc787e250a131"},
+ {file = "numpy-2.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:49ca4decb342d66018b01932139c0961a8f9ddc7589611158cb3c27cbcf76448"},
+ {file = "numpy-2.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:11a76c372d1d37437857280aa142086476136a8c0f373b2e648ab2c8f18fb195"},
+ {file = "numpy-2.0.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:807ec44583fd708a21d4a11d94aedf2f4f3c3719035c76a2bbe1fe8e217bdc57"},
+ {file = "numpy-2.0.2-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:8cafab480740e22f8d833acefed5cc87ce276f4ece12fdaa2e8903db2f82897a"},
+ {file = "numpy-2.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a15f476a45e6e5a3a79d8a14e62161d27ad897381fecfa4a09ed5322f2085669"},
+ {file = "numpy-2.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13e689d772146140a252c3a28501da66dfecd77490b498b168b501835041f951"},
+ {file = "numpy-2.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9ea91dfb7c3d1c56a0e55657c0afb38cf1eeae4544c208dc465c3c9f3a7c09f9"},
+ {file = "numpy-2.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c1c9307701fec8f3f7a1e6711f9089c06e6284b3afbbcd259f7791282d660a15"},
+ {file = "numpy-2.0.2-cp311-cp311-win32.whl", hash = "sha256:a392a68bd329eafac5817e5aefeb39038c48b671afd242710b451e76090e81f4"},
+ {file = "numpy-2.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:286cd40ce2b7d652a6f22efdfc6d1edf879440e53e76a75955bc0c826c7e64dc"},
+ {file = "numpy-2.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:df55d490dea7934f330006d0f81e8551ba6010a5bf035a249ef61a94f21c500b"},
+ {file = "numpy-2.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8df823f570d9adf0978347d1f926b2a867d5608f434a7cff7f7908c6570dcf5e"},
+ {file = "numpy-2.0.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:9a92ae5c14811e390f3767053ff54eaee3bf84576d99a2456391401323f4ec2c"},
+ {file = "numpy-2.0.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:a842d573724391493a97a62ebbb8e731f8a5dcc5d285dfc99141ca15a3302d0c"},
+ {file = "numpy-2.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c05e238064fc0610c840d1cf6a13bf63d7e391717d247f1bf0318172e759e692"},
+ {file = "numpy-2.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0123ffdaa88fa4ab64835dcbde75dcdf89c453c922f18dced6e27c90d1d0ec5a"},
+ {file = "numpy-2.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:96a55f64139912d61de9137f11bf39a55ec8faec288c75a54f93dfd39f7eb40c"},
+ {file = "numpy-2.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ec9852fb39354b5a45a80bdab5ac02dd02b15f44b3804e9f00c556bf24b4bded"},
+ {file = "numpy-2.0.2-cp312-cp312-win32.whl", hash = "sha256:671bec6496f83202ed2d3c8fdc486a8fc86942f2e69ff0e986140339a63bcbe5"},
+ {file = "numpy-2.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:cfd41e13fdc257aa5778496b8caa5e856dc4896d4ccf01841daee1d96465467a"},
+ {file = "numpy-2.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9059e10581ce4093f735ed23f3b9d283b9d517ff46009ddd485f1747eb22653c"},
+ {file = "numpy-2.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:423e89b23490805d2a5a96fe40ec507407b8ee786d66f7328be214f9679df6dd"},
+ {file = "numpy-2.0.2-cp39-cp39-macosx_14_0_arm64.whl", hash = "sha256:2b2955fa6f11907cf7a70dab0d0755159bca87755e831e47932367fc8f2f2d0b"},
+ {file = "numpy-2.0.2-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:97032a27bd9d8988b9a97a8c4d2c9f2c15a81f61e2f21404d7e8ef00cb5be729"},
+ {file = "numpy-2.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e795a8be3ddbac43274f18588329c72939870a16cae810c2b73461c40718ab1"},
+ {file = "numpy-2.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f26b258c385842546006213344c50655ff1555a9338e2e5e02a0756dc3e803dd"},
+ {file = "numpy-2.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5fec9451a7789926bcf7c2b8d187292c9f93ea30284802a0ab3f5be8ab36865d"},
+ {file = "numpy-2.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:9189427407d88ff25ecf8f12469d4d39d35bee1db5d39fc5c168c6f088a6956d"},
+ {file = "numpy-2.0.2-cp39-cp39-win32.whl", hash = "sha256:905d16e0c60200656500c95b6b8dca5d109e23cb24abc701d41c02d74c6b3afa"},
+ {file = "numpy-2.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:a3f4ab0caa7f053f6797fcd4e1e25caee367db3112ef2b6ef82d749530768c73"},
+ {file = "numpy-2.0.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:7f0a0c6f12e07fa94133c8a67404322845220c06a9e80e85999afe727f7438b8"},
+ {file = "numpy-2.0.2-pp39-pypy39_pp73-macosx_14_0_x86_64.whl", hash = "sha256:312950fdd060354350ed123c0e25a71327d3711584beaef30cdaa93320c392d4"},
+ {file = "numpy-2.0.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26df23238872200f63518dd2aa984cfca675d82469535dc7162dc2ee52d9dd5c"},
+ {file = "numpy-2.0.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a46288ec55ebbd58947d31d72be2c63cbf839f0a63b49cb755022310792a3385"},
+ {file = "numpy-2.0.2.tar.gz", hash = "sha256:883c987dee1880e2a864ab0dc9892292582510604156762362d9326444636e78"},
+]
+
+[[package]]
+name = "numpy"
+version = "2.3.2"
+description = "Fundamental package for array computing in Python"
+optional = false
+python-versions = ">=3.11"
+groups = ["main"]
+markers = "python_version >= \"3.11\""
+files = [
+ {file = "numpy-2.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:852ae5bed3478b92f093e30f785c98e0cb62fa0a939ed057c31716e18a7a22b9"},
+ {file = "numpy-2.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7a0e27186e781a69959d0230dd9909b5e26024f8da10683bd6344baea1885168"},
+ {file = "numpy-2.3.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:f0a1a8476ad77a228e41619af2fa9505cf69df928e9aaa165746584ea17fed2b"},
+ {file = "numpy-2.3.2-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:cbc95b3813920145032412f7e33d12080f11dc776262df1712e1638207dde9e8"},
+ {file = "numpy-2.3.2-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f75018be4980a7324edc5930fe39aa391d5734531b1926968605416ff58c332d"},
+ {file = "numpy-2.3.2-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:20b8200721840f5621b7bd03f8dcd78de33ec522fc40dc2641aa09537df010c3"},
+ {file = "numpy-2.3.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1f91e5c028504660d606340a084db4b216567ded1056ea2b4be4f9d10b67197f"},
+ {file = "numpy-2.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:fb1752a3bb9a3ad2d6b090b88a9a0ae1cd6f004ef95f75825e2f382c183b2097"},
+ {file = "numpy-2.3.2-cp311-cp311-win32.whl", hash = "sha256:4ae6863868aaee2f57503c7a5052b3a2807cf7a3914475e637a0ecd366ced220"},
+ {file = "numpy-2.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:240259d6564f1c65424bcd10f435145a7644a65a6811cfc3201c4a429ba79170"},
+ {file = "numpy-2.3.2-cp311-cp311-win_arm64.whl", hash = "sha256:4209f874d45f921bde2cff1ffcd8a3695f545ad2ffbef6d3d3c6768162efab89"},
+ {file = "numpy-2.3.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:bc3186bea41fae9d8e90c2b4fb5f0a1f5a690682da79b92574d63f56b529080b"},
+ {file = "numpy-2.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2f4f0215edb189048a3c03bd5b19345bdfa7b45a7a6f72ae5945d2a28272727f"},
+ {file = "numpy-2.3.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:8b1224a734cd509f70816455c3cffe13a4f599b1bf7130f913ba0e2c0b2006c0"},
+ {file = "numpy-2.3.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:3dcf02866b977a38ba3ec10215220609ab9667378a9e2150615673f3ffd6c73b"},
+ {file = "numpy-2.3.2-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:572d5512df5470f50ada8d1972c5f1082d9a0b7aa5944db8084077570cf98370"},
+ {file = "numpy-2.3.2-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8145dd6d10df13c559d1e4314df29695613575183fa2e2d11fac4c208c8a1f73"},
+ {file = "numpy-2.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:103ea7063fa624af04a791c39f97070bf93b96d7af7eb23530cd087dc8dbe9dc"},
+ {file = "numpy-2.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fc927d7f289d14f5e037be917539620603294454130b6de200091e23d27dc9be"},
+ {file = "numpy-2.3.2-cp312-cp312-win32.whl", hash = "sha256:d95f59afe7f808c103be692175008bab926b59309ade3e6d25009e9a171f7036"},
+ {file = "numpy-2.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:9e196ade2400c0c737d93465327d1ae7c06c7cb8a1756121ebf54b06ca183c7f"},
+ {file = "numpy-2.3.2-cp312-cp312-win_arm64.whl", hash = "sha256:ee807923782faaf60d0d7331f5e86da7d5e3079e28b291973c545476c2b00d07"},
+ {file = "numpy-2.3.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c8d9727f5316a256425892b043736d63e89ed15bbfe6556c5ff4d9d4448ff3b3"},
+ {file = "numpy-2.3.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:efc81393f25f14d11c9d161e46e6ee348637c0a1e8a54bf9dedc472a3fae993b"},
+ {file = "numpy-2.3.2-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:dd937f088a2df683cbb79dda9a772b62a3e5a8a7e76690612c2737f38c6ef1b6"},
+ {file = "numpy-2.3.2-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:11e58218c0c46c80509186e460d79fbdc9ca1eb8d8aee39d8f2dc768eb781089"},
+ {file = "numpy-2.3.2-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5ad4ebcb683a1f99f4f392cc522ee20a18b2bb12a2c1c42c3d48d5a1adc9d3d2"},
+ {file = "numpy-2.3.2-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:938065908d1d869c7d75d8ec45f735a034771c6ea07088867f713d1cd3bbbe4f"},
+ {file = "numpy-2.3.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:66459dccc65d8ec98cc7df61307b64bf9e08101f9598755d42d8ae65d9a7a6ee"},
+ {file = "numpy-2.3.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a7af9ed2aa9ec5950daf05bb11abc4076a108bd3c7db9aa7251d5f107079b6a6"},
+ {file = "numpy-2.3.2-cp313-cp313-win32.whl", hash = "sha256:906a30249315f9c8e17b085cc5f87d3f369b35fedd0051d4a84686967bdbbd0b"},
+ {file = "numpy-2.3.2-cp313-cp313-win_amd64.whl", hash = "sha256:c63d95dc9d67b676e9108fe0d2182987ccb0f11933c1e8959f42fa0da8d4fa56"},
+ {file = "numpy-2.3.2-cp313-cp313-win_arm64.whl", hash = "sha256:b05a89f2fb84d21235f93de47129dd4f11c16f64c87c33f5e284e6a3a54e43f2"},
+ {file = "numpy-2.3.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:4e6ecfeddfa83b02318f4d84acf15fbdbf9ded18e46989a15a8b6995dfbf85ab"},
+ {file = "numpy-2.3.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:508b0eada3eded10a3b55725b40806a4b855961040180028f52580c4729916a2"},
+ {file = "numpy-2.3.2-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:754d6755d9a7588bdc6ac47dc4ee97867271b17cee39cb87aef079574366db0a"},
+ {file = "numpy-2.3.2-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:a9f66e7d2b2d7712410d3bc5684149040ef5f19856f20277cd17ea83e5006286"},
+ {file = "numpy-2.3.2-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:de6ea4e5a65d5a90c7d286ddff2b87f3f4ad61faa3db8dabe936b34c2275b6f8"},
+ {file = "numpy-2.3.2-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a3ef07ec8cbc8fc9e369c8dcd52019510c12da4de81367d8b20bc692aa07573a"},
+ {file = "numpy-2.3.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:27c9f90e7481275c7800dc9c24b7cc40ace3fdb970ae4d21eaff983a32f70c91"},
+ {file = "numpy-2.3.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:07b62978075b67eee4065b166d000d457c82a1efe726cce608b9db9dd66a73a5"},
+ {file = "numpy-2.3.2-cp313-cp313t-win32.whl", hash = "sha256:c771cfac34a4f2c0de8e8c97312d07d64fd8f8ed45bc9f5726a7e947270152b5"},
+ {file = "numpy-2.3.2-cp313-cp313t-win_amd64.whl", hash = "sha256:72dbebb2dcc8305c431b2836bcc66af967df91be793d63a24e3d9b741374c450"},
+ {file = "numpy-2.3.2-cp313-cp313t-win_arm64.whl", hash = "sha256:72c6df2267e926a6d5286b0a6d556ebe49eae261062059317837fda12ddf0c1a"},
+ {file = "numpy-2.3.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:448a66d052d0cf14ce9865d159bfc403282c9bc7bb2a31b03cc18b651eca8b1a"},
+ {file = "numpy-2.3.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:546aaf78e81b4081b2eba1d105c3b34064783027a06b3ab20b6eba21fb64132b"},
+ {file = "numpy-2.3.2-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:87c930d52f45df092f7578889711a0768094debf73cfcde105e2d66954358125"},
+ {file = "numpy-2.3.2-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:8dc082ea901a62edb8f59713c6a7e28a85daddcb67454c839de57656478f5b19"},
+ {file = "numpy-2.3.2-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:af58de8745f7fa9ca1c0c7c943616c6fe28e75d0c81f5c295810e3c83b5be92f"},
+ {file = "numpy-2.3.2-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed5527c4cf10f16c6d0b6bee1f89958bccb0ad2522c8cadc2efd318bcd545f5"},
+ {file = "numpy-2.3.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:095737ed986e00393ec18ec0b21b47c22889ae4b0cd2d5e88342e08b01141f58"},
+ {file = "numpy-2.3.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b5e40e80299607f597e1a8a247ff8d71d79c5b52baa11cc1cce30aa92d2da6e0"},
+ {file = "numpy-2.3.2-cp314-cp314-win32.whl", hash = "sha256:7d6e390423cc1f76e1b8108c9b6889d20a7a1f59d9a60cac4a050fa734d6c1e2"},
+ {file = "numpy-2.3.2-cp314-cp314-win_amd64.whl", hash = "sha256:b9d0878b21e3918d76d2209c924ebb272340da1fb51abc00f986c258cd5e957b"},
+ {file = "numpy-2.3.2-cp314-cp314-win_arm64.whl", hash = "sha256:2738534837c6a1d0c39340a190177d7d66fdf432894f469728da901f8f6dc910"},
+ {file = "numpy-2.3.2-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:4d002ecf7c9b53240be3bb69d80f86ddbd34078bae04d87be81c1f58466f264e"},
+ {file = "numpy-2.3.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:293b2192c6bcce487dbc6326de5853787f870aeb6c43f8f9c6496db5b1781e45"},
+ {file = "numpy-2.3.2-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:0a4f2021a6da53a0d580d6ef5db29947025ae8b35b3250141805ea9a32bbe86b"},
+ {file = "numpy-2.3.2-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:9c144440db4bf3bb6372d2c3e49834cc0ff7bb4c24975ab33e01199e645416f2"},
+ {file = "numpy-2.3.2-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f92d6c2a8535dc4fe4419562294ff957f83a16ebdec66df0805e473ffaad8bd0"},
+ {file = "numpy-2.3.2-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cefc2219baa48e468e3db7e706305fcd0c095534a192a08f31e98d83a7d45fb0"},
+ {file = "numpy-2.3.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:76c3e9501ceb50b2ff3824c3589d5d1ab4ac857b0ee3f8f49629d0de55ecf7c2"},
+ {file = "numpy-2.3.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:122bf5ed9a0221b3419672493878ba4967121514b1d7d4656a7580cd11dddcbf"},
+ {file = "numpy-2.3.2-cp314-cp314t-win32.whl", hash = "sha256:6f1ae3dcb840edccc45af496f312528c15b1f79ac318169d094e85e4bb35fdf1"},
+ {file = "numpy-2.3.2-cp314-cp314t-win_amd64.whl", hash = "sha256:087ffc25890d89a43536f75c5fe8770922008758e8eeeef61733957041ed2f9b"},
+ {file = "numpy-2.3.2-cp314-cp314t-win_arm64.whl", hash = "sha256:092aeb3449833ea9c0bf0089d70c29ae480685dd2377ec9cdbbb620257f84631"},
+ {file = "numpy-2.3.2-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:14a91ebac98813a49bc6aa1a0dfc09513dcec1d97eaf31ca21a87221a1cdcb15"},
+ {file = "numpy-2.3.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:71669b5daae692189540cffc4c439468d35a3f84f0c88b078ecd94337f6cb0ec"},
+ {file = "numpy-2.3.2-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:69779198d9caee6e547adb933941ed7520f896fd9656834c300bdf4dd8642712"},
+ {file = "numpy-2.3.2-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:2c3271cc4097beb5a60f010bcc1cc204b300bb3eafb4399376418a83a1c6373c"},
+ {file = "numpy-2.3.2-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8446acd11fe3dc1830568c941d44449fd5cb83068e5c70bd5a470d323d448296"},
+ {file = "numpy-2.3.2-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:aa098a5ab53fa407fded5870865c6275a5cd4101cfdef8d6fafc48286a96e981"},
+ {file = "numpy-2.3.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:6936aff90dda378c09bea075af0d9c675fe3a977a9d2402f95a87f440f59f619"},
+ {file = "numpy-2.3.2.tar.gz", hash = "sha256:e0486a11ec30cdecb53f184d496d1c6a20786c81e55e41640270130056f8ee48"},
+]
+
+[[package]]
+name = "nvidia-cublas-cu12"
+version = "12.6.4.1"
+description = "CUBLAS native runtime libraries"
+optional = false
+python-versions = ">=3"
+groups = ["main"]
+markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version >= \"3.12\""
+files = [
+ {file = "nvidia_cublas_cu12-12.6.4.1-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:08ed2686e9875d01b58e3cb379c6896df8e76c75e0d4a7f7dace3d7b6d9ef8eb"},
+ {file = "nvidia_cublas_cu12-12.6.4.1-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:235f728d6e2a409eddf1df58d5b0921cf80cfa9e72b9f2775ccb7b4a87984668"},
+ {file = "nvidia_cublas_cu12-12.6.4.1-py3-none-win_amd64.whl", hash = "sha256:9e4fa264f4d8a4eb0cdbd34beadc029f453b3bafae02401e999cf3d5a5af75f8"},
+]
+
+[[package]]
+name = "nvidia-cublas-cu12"
+version = "12.8.4.1"
+description = "CUBLAS native runtime libraries"
+optional = false
+python-versions = ">=3"
+groups = ["main"]
+markers = "platform_machine == \"x86_64\" and platform_system == \"Linux\" and python_version <= \"3.11\""
+files = [
+ {file = "nvidia_cublas_cu12-12.8.4.1-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:b86f6dd8935884615a0683b663891d43781b819ac4f2ba2b0c9604676af346d0"},
+ {file = "nvidia_cublas_cu12-12.8.4.1-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:8ac4e771d5a348c551b2a426eda6193c19aa630236b418086020df5ba9667142"},
+ {file = "nvidia_cublas_cu12-12.8.4.1-py3-none-win_amd64.whl", hash = "sha256:47e9b82132fa8d2b4944e708049229601448aaad7e6f296f630f2d1a32de35af"},
+]
+
+[[package]]
+name = "nvidia-cuda-cupti-cu12"
+version = "12.6.80"
+description = "CUDA profiling tools runtime libs."
+optional = false
+python-versions = ">=3"
+groups = ["main"]
+markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version >= \"3.12\""
+files = [
+ {file = "nvidia_cuda_cupti_cu12-12.6.80-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:166ee35a3ff1587f2490364f90eeeb8da06cd867bd5b701bf7f9a02b78bc63fc"},
+ {file = "nvidia_cuda_cupti_cu12-12.6.80-py3-none-manylinux2014_aarch64.whl", hash = "sha256:358b4a1d35370353d52e12f0a7d1769fc01ff74a191689d3870b2123156184c4"},
+ {file = "nvidia_cuda_cupti_cu12-12.6.80-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6768bad6cab4f19e8292125e5f1ac8aa7d1718704012a0e3272a6f61c4bce132"},
+ {file = "nvidia_cuda_cupti_cu12-12.6.80-py3-none-manylinux2014_x86_64.whl", hash = "sha256:a3eff6cdfcc6a4c35db968a06fcadb061cbc7d6dde548609a941ff8701b98b73"},
+ {file = "nvidia_cuda_cupti_cu12-12.6.80-py3-none-win_amd64.whl", hash = "sha256:bbe6ae76e83ce5251b56e8c8e61a964f757175682bbad058b170b136266ab00a"},
+]
+
+[[package]]
+name = "nvidia-cuda-cupti-cu12"
+version = "12.8.90"
+description = "CUDA profiling tools runtime libs."
+optional = false
+python-versions = ">=3"
+groups = ["main"]
+markers = "platform_machine == \"x86_64\" and platform_system == \"Linux\" and python_version <= \"3.11\""
+files = [
+ {file = "nvidia_cuda_cupti_cu12-12.8.90-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4412396548808ddfed3f17a467b104ba7751e6b58678a4b840675c56d21cf7ed"},
+ {file = "nvidia_cuda_cupti_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ea0cb07ebda26bb9b29ba82cda34849e73c166c18162d3913575b0c9db9a6182"},
+ {file = "nvidia_cuda_cupti_cu12-12.8.90-py3-none-win_amd64.whl", hash = "sha256:bb479dcdf7e6d4f8b0b01b115260399bf34154a1a2e9fe11c85c517d87efd98e"},
+]
+
+[[package]]
+name = "nvidia-cuda-nvrtc-cu12"
+version = "12.6.77"
+description = "NVRTC native runtime libraries"
+optional = false
+python-versions = ">=3"
+groups = ["main"]
+markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version >= \"3.12\""
+files = [
+ {file = "nvidia_cuda_nvrtc_cu12-12.6.77-py3-none-manylinux2014_aarch64.whl", hash = "sha256:5847f1d6e5b757f1d2b3991a01082a44aad6f10ab3c5c0213fa3e25bddc25a13"},
+ {file = "nvidia_cuda_nvrtc_cu12-12.6.77-py3-none-manylinux2014_x86_64.whl", hash = "sha256:35b0cc6ee3a9636d5409133e79273ce1f3fd087abb0532d2d2e8fff1fe9efc53"},
+ {file = "nvidia_cuda_nvrtc_cu12-12.6.77-py3-none-win_amd64.whl", hash = "sha256:f7007dbd914c56bd80ea31bc43e8e149da38f68158f423ba845fc3292684e45a"},
+]
+
+[[package]]
+name = "nvidia-cuda-nvrtc-cu12"
+version = "12.8.93"
+description = "NVRTC native runtime libraries"
+optional = false
+python-versions = ">=3"
+groups = ["main"]
+markers = "platform_machine == \"x86_64\" and platform_system == \"Linux\" and python_version <= \"3.11\""
+files = [
+ {file = "nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:a7756528852ef889772a84c6cd89d41dfa74667e24cca16bb31f8f061e3e9994"},
+ {file = "nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fc1fec1e1637854b4c0a65fb9a8346b51dd9ee69e61ebaccc82058441f15bce8"},
+ {file = "nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-win_amd64.whl", hash = "sha256:7a4b6b2904850fe78e0bd179c4b655c404d4bb799ef03ddc60804247099ae909"},
+]
+
+[[package]]
+name = "nvidia-cuda-runtime-cu12"
+version = "12.6.77"
+description = "CUDA Runtime native Libraries"
+optional = false
+python-versions = ">=3"
+groups = ["main"]
+markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version >= \"3.12\""
+files = [
+ {file = "nvidia_cuda_runtime_cu12-12.6.77-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:6116fad3e049e04791c0256a9778c16237837c08b27ed8c8401e2e45de8d60cd"},
+ {file = "nvidia_cuda_runtime_cu12-12.6.77-py3-none-manylinux2014_aarch64.whl", hash = "sha256:d461264ecb429c84c8879a7153499ddc7b19b5f8d84c204307491989a365588e"},
+ {file = "nvidia_cuda_runtime_cu12-12.6.77-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ba3b56a4f896141e25e19ab287cd71e52a6a0f4b29d0d31609f60e3b4d5219b7"},
+ {file = "nvidia_cuda_runtime_cu12-12.6.77-py3-none-manylinux2014_x86_64.whl", hash = "sha256:a84d15d5e1da416dd4774cb42edf5e954a3e60cc945698dc1d5be02321c44dc8"},
+ {file = "nvidia_cuda_runtime_cu12-12.6.77-py3-none-win_amd64.whl", hash = "sha256:86c58044c824bf3c173c49a2dbc7a6c8b53cb4e4dca50068be0bf64e9dab3f7f"},
+]
+
+[[package]]
+name = "nvidia-cuda-runtime-cu12"
+version = "12.8.90"
+description = "CUDA Runtime native Libraries"
+optional = false
+python-versions = ">=3"
+groups = ["main"]
+markers = "platform_machine == \"x86_64\" and platform_system == \"Linux\" and python_version <= \"3.11\""
+files = [
+ {file = "nvidia_cuda_runtime_cu12-12.8.90-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:52bf7bbee900262ffefe5e9d5a2a69a30d97e2bc5bb6cc866688caa976966e3d"},
+ {file = "nvidia_cuda_runtime_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:adade8dcbd0edf427b7204d480d6066d33902cab2a4707dcfc48a2d0fd44ab90"},
+ {file = "nvidia_cuda_runtime_cu12-12.8.90-py3-none-win_amd64.whl", hash = "sha256:c0c6027f01505bfed6c3b21ec546f69c687689aad5f1a377554bc6ca4aa993a8"},
+]
+
+[[package]]
+name = "nvidia-cudnn-cu12"
+version = "9.5.1.17"
+description = "cuDNN runtime libraries"
+optional = false
+python-versions = ">=3"
+groups = ["main"]
+markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version >= \"3.12\""
+files = [
+ {file = "nvidia_cudnn_cu12-9.5.1.17-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:9fd4584468533c61873e5fda8ca41bac3a38bcb2d12350830c69b0a96a7e4def"},
+ {file = "nvidia_cudnn_cu12-9.5.1.17-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:30ac3869f6db17d170e0e556dd6cc5eee02647abc31ca856634d5a40f82c15b2"},
+ {file = "nvidia_cudnn_cu12-9.5.1.17-py3-none-win_amd64.whl", hash = "sha256:d7af0f8a4f3b4b9dbb3122f2ef553b45694ed9c384d5a75bab197b8eefb79ab8"},
+]
+
+[package.dependencies]
+nvidia-cublas-cu12 = "*"
+
+[[package]]
+name = "nvidia-cudnn-cu12"
+version = "9.10.2.21"
+description = "cuDNN runtime libraries"
+optional = false
+python-versions = ">=3"
+groups = ["main"]
+markers = "platform_machine == \"x86_64\" and platform_system == \"Linux\" and python_version <= \"3.11\""
+files = [
+ {file = "nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:c9132cc3f8958447b4910a1720036d9eff5928cc3179b0a51fb6d167c6cc87d8"},
+ {file = "nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:949452be657fa16687d0930933f032835951ef0892b37d2d53824d1a84dc97a8"},
+ {file = "nvidia_cudnn_cu12-9.10.2.21-py3-none-win_amd64.whl", hash = "sha256:c6288de7d63e6cf62988f0923f96dc339cea362decb1bf5b3141883392a7d65e"},
+]
+
+[package.dependencies]
+nvidia-cublas-cu12 = "*"
+
+[[package]]
+name = "nvidia-cufft-cu12"
+version = "11.3.0.4"
+description = "CUFFT native runtime libraries"
+optional = false
+python-versions = ">=3"
+groups = ["main"]
+markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version >= \"3.12\""
+files = [
+ {file = "nvidia_cufft_cu12-11.3.0.4-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d16079550df460376455cba121db6564089176d9bac9e4f360493ca4741b22a6"},
+ {file = "nvidia_cufft_cu12-11.3.0.4-py3-none-manylinux2014_aarch64.whl", hash = "sha256:8510990de9f96c803a051822618d42bf6cb8f069ff3f48d93a8486efdacb48fb"},
+ {file = "nvidia_cufft_cu12-11.3.0.4-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ccba62eb9cef5559abd5e0d54ceed2d9934030f51163df018532142a8ec533e5"},
+ {file = "nvidia_cufft_cu12-11.3.0.4-py3-none-manylinux2014_x86_64.whl", hash = "sha256:768160ac89f6f7b459bee747e8d175dbf53619cfe74b2a5636264163138013ca"},
+ {file = "nvidia_cufft_cu12-11.3.0.4-py3-none-win_amd64.whl", hash = "sha256:6048ebddfb90d09d2707efb1fd78d4e3a77cb3ae4dc60e19aab6be0ece2ae464"},
+]
+
+[package.dependencies]
+nvidia-nvjitlink-cu12 = "*"
+
+[[package]]
+name = "nvidia-cufft-cu12"
+version = "11.3.3.83"
+description = "CUFFT native runtime libraries"
+optional = false
+python-versions = ">=3"
+groups = ["main"]
+markers = "platform_machine == \"x86_64\" and platform_system == \"Linux\" and python_version <= \"3.11\""
+files = [
+ {file = "nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:848ef7224d6305cdb2a4df928759dca7b1201874787083b6e7550dd6765ce69a"},
+ {file = "nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d2dd21ec0b88cf61b62e6b43564355e5222e4a3fb394cac0db101f2dd0d4f74"},
+ {file = "nvidia_cufft_cu12-11.3.3.83-py3-none-win_amd64.whl", hash = "sha256:7a64a98ef2a7c47f905aaf8931b69a3a43f27c55530c698bb2ed7c75c0b42cb7"},
+]
+
+[package.dependencies]
+nvidia-nvjitlink-cu12 = "*"
+
+[[package]]
+name = "nvidia-cufile-cu12"
+version = "1.11.1.6"
+description = "cuFile GPUDirect libraries"
+optional = false
+python-versions = ">=3"
+groups = ["main"]
+markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version >= \"3.12\""
+files = [
+ {file = "nvidia_cufile_cu12-1.11.1.6-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cc23469d1c7e52ce6c1d55253273d32c565dd22068647f3aa59b3c6b005bf159"},
+ {file = "nvidia_cufile_cu12-1.11.1.6-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:8f57a0051dcf2543f6dc2b98a98cb2719c37d3cee1baba8965d57f3bbc90d4db"},
+]
+
+[[package]]
+name = "nvidia-cufile-cu12"
+version = "1.13.1.3"
+description = "cuFile GPUDirect libraries"
+optional = false
+python-versions = ">=3"
+groups = ["main"]
+markers = "platform_machine == \"x86_64\" and platform_system == \"Linux\" and python_version <= \"3.11\""
+files = [
+ {file = "nvidia_cufile_cu12-1.13.1.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1d069003be650e131b21c932ec3d8969c1715379251f8d23a1860554b1cb24fc"},
+ {file = "nvidia_cufile_cu12-1.13.1.3-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:4beb6d4cce47c1a0f1013d72e02b0994730359e17801d395bdcbf20cfb3bb00a"},
+]
+
+[[package]]
+name = "nvidia-curand-cu12"
+version = "10.3.7.77"
+description = "CURAND native runtime libraries"
+optional = false
+python-versions = ">=3"
+groups = ["main"]
+markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version >= \"3.12\""
+files = [
+ {file = "nvidia_curand_cu12-10.3.7.77-py3-none-manylinux2014_aarch64.whl", hash = "sha256:6e82df077060ea28e37f48a3ec442a8f47690c7499bff392a5938614b56c98d8"},
+ {file = "nvidia_curand_cu12-10.3.7.77-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a42cd1344297f70b9e39a1e4f467a4e1c10f1da54ff7a85c12197f6c652c8bdf"},
+ {file = "nvidia_curand_cu12-10.3.7.77-py3-none-manylinux2014_x86_64.whl", hash = "sha256:99f1a32f1ac2bd134897fc7a203f779303261268a65762a623bf30cc9fe79117"},
+ {file = "nvidia_curand_cu12-10.3.7.77-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:7b2ed8e95595c3591d984ea3603dd66fe6ce6812b886d59049988a712ed06b6e"},
+ {file = "nvidia_curand_cu12-10.3.7.77-py3-none-win_amd64.whl", hash = "sha256:6d6d935ffba0f3d439b7cd968192ff068fafd9018dbf1b85b37261b13cfc9905"},
+]
+
+[[package]]
+name = "nvidia-curand-cu12"
+version = "10.3.9.90"
+description = "CURAND native runtime libraries"
+optional = false
+python-versions = ">=3"
+groups = ["main"]
+markers = "platform_machine == \"x86_64\" and platform_system == \"Linux\" and python_version <= \"3.11\""
+files = [
+ {file = "nvidia_curand_cu12-10.3.9.90-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:dfab99248034673b779bc6decafdc3404a8a6f502462201f2f31f11354204acd"},
+ {file = "nvidia_curand_cu12-10.3.9.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:b32331d4f4df5d6eefa0554c565b626c7216f87a06a4f56fab27c3b68a830ec9"},
+ {file = "nvidia_curand_cu12-10.3.9.90-py3-none-win_amd64.whl", hash = "sha256:f149a8ca457277da854f89cf282d6ef43176861926c7ac85b2a0fbd237c587ec"},
+]
+
+[[package]]
+name = "nvidia-cusolver-cu12"
+version = "11.7.1.2"
+description = "CUDA solver native runtime libraries"
+optional = false
+python-versions = ">=3"
+groups = ["main"]
+markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version >= \"3.12\""
+files = [
+ {file = "nvidia_cusolver_cu12-11.7.1.2-py3-none-manylinux2014_aarch64.whl", hash = "sha256:0ce237ef60acde1efc457335a2ddadfd7610b892d94efee7b776c64bb1cac9e0"},
+ {file = "nvidia_cusolver_cu12-11.7.1.2-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e9e49843a7707e42022babb9bcfa33c29857a93b88020c4e4434656a655b698c"},
+ {file = "nvidia_cusolver_cu12-11.7.1.2-py3-none-manylinux2014_x86_64.whl", hash = "sha256:6cf28f17f64107a0c4d7802be5ff5537b2130bfc112f25d5a30df227058ca0e6"},
+ {file = "nvidia_cusolver_cu12-11.7.1.2-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:dbbe4fc38ec1289c7e5230e16248365e375c3673c9c8bac5796e2e20db07f56e"},
+ {file = "nvidia_cusolver_cu12-11.7.1.2-py3-none-win_amd64.whl", hash = "sha256:6813f9d8073f555444a8705f3ab0296d3e1cb37a16d694c5fc8b862a0d8706d7"},
+]
+
+[package.dependencies]
+nvidia-cublas-cu12 = "*"
+nvidia-cusparse-cu12 = "*"
+nvidia-nvjitlink-cu12 = "*"
+
+[[package]]
+name = "nvidia-cusolver-cu12"
+version = "11.7.3.90"
+description = "CUDA solver native runtime libraries"
+optional = false
+python-versions = ">=3"
+groups = ["main"]
+markers = "platform_machine == \"x86_64\" and platform_system == \"Linux\" and python_version <= \"3.11\""
+files = [
+ {file = "nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:db9ed69dbef9715071232caa9b69c52ac7de3a95773c2db65bdba85916e4e5c0"},
+ {file = "nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:4376c11ad263152bd50ea295c05370360776f8c3427b30991df774f9fb26c450"},
+ {file = "nvidia_cusolver_cu12-11.7.3.90-py3-none-win_amd64.whl", hash = "sha256:4a550db115fcabc4d495eb7d39ac8b58d4ab5d8e63274d3754df1c0ad6a22d34"},
+]
+
+[package.dependencies]
+nvidia-cublas-cu12 = "*"
+nvidia-cusparse-cu12 = "*"
+nvidia-nvjitlink-cu12 = "*"
+
+[[package]]
+name = "nvidia-cusparse-cu12"
+version = "12.5.4.2"
+description = "CUSPARSE native runtime libraries"
+optional = false
+python-versions = ">=3"
+groups = ["main"]
+markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version >= \"3.12\""
+files = [
+ {file = "nvidia_cusparse_cu12-12.5.4.2-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d25b62fb18751758fe3c93a4a08eff08effedfe4edf1c6bb5afd0890fe88f887"},
+ {file = "nvidia_cusparse_cu12-12.5.4.2-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7aa32fa5470cf754f72d1116c7cbc300b4e638d3ae5304cfa4a638a5b87161b1"},
+ {file = "nvidia_cusparse_cu12-12.5.4.2-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7556d9eca156e18184b94947ade0fba5bb47d69cec46bf8660fd2c71a4b48b73"},
+ {file = "nvidia_cusparse_cu12-12.5.4.2-py3-none-manylinux2014_x86_64.whl", hash = "sha256:23749a6571191a215cb74d1cdbff4a86e7b19f1200c071b3fcf844a5bea23a2f"},
+ {file = "nvidia_cusparse_cu12-12.5.4.2-py3-none-win_amd64.whl", hash = "sha256:4acb8c08855a26d737398cba8fb6f8f5045d93f82612b4cfd84645a2332ccf20"},
+]
+
+[package.dependencies]
+nvidia-nvjitlink-cu12 = "*"
+
+[[package]]
+name = "nvidia-cusparse-cu12"
+version = "12.5.8.93"
+description = "CUSPARSE native runtime libraries"
+optional = false
+python-versions = ">=3"
+groups = ["main"]
+markers = "platform_machine == \"x86_64\" and platform_system == \"Linux\" and python_version <= \"3.11\""
+files = [
+ {file = "nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:9b6c161cb130be1a07a27ea6923df8141f3c295852f4b260c65f18f3e0a091dc"},
+ {file = "nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1ec05d76bbbd8b61b06a80e1eaf8cf4959c3d4ce8e711b65ebd0443bb0ebb13b"},
+ {file = "nvidia_cusparse_cu12-12.5.8.93-py3-none-win_amd64.whl", hash = "sha256:9a33604331cb2cac199f2e7f5104dfbb8a5a898c367a53dfda9ff2acb6b6b4dd"},
+]
+
+[package.dependencies]
+nvidia-nvjitlink-cu12 = "*"
+
+[[package]]
+name = "nvidia-cusparselt-cu12"
+version = "0.6.3"
+description = "NVIDIA cuSPARSELt"
+optional = false
+python-versions = "*"
+groups = ["main"]
+markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version >= \"3.12\""
+files = [
+ {file = "nvidia_cusparselt_cu12-0.6.3-py3-none-manylinux2014_aarch64.whl", hash = "sha256:8371549623ba601a06322af2133c4a44350575f5a3108fb75f3ef20b822ad5f1"},
+ {file = "nvidia_cusparselt_cu12-0.6.3-py3-none-manylinux2014_x86_64.whl", hash = "sha256:e5c8a26c36445dd2e6812f1177978a24e2d37cacce7e090f297a688d1ec44f46"},
+ {file = "nvidia_cusparselt_cu12-0.6.3-py3-none-win_amd64.whl", hash = "sha256:3b325bcbd9b754ba43df5a311488fca11a6b5dc3d11df4d190c000cf1a0765c7"},
+]
+
+[[package]]
+name = "nvidia-cusparselt-cu12"
+version = "0.7.1"
+description = "NVIDIA cuSPARSELt"
+optional = false
+python-versions = "*"
+groups = ["main"]
+markers = "platform_machine == \"x86_64\" and platform_system == \"Linux\" and python_version <= \"3.11\""
+files = [
+ {file = "nvidia_cusparselt_cu12-0.7.1-py3-none-manylinux2014_aarch64.whl", hash = "sha256:8878dce784d0fac90131b6817b607e803c36e629ba34dc5b433471382196b6a5"},
+ {file = "nvidia_cusparselt_cu12-0.7.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:f1bb701d6b930d5a7cea44c19ceb973311500847f81b634d802b7b539dc55623"},
+ {file = "nvidia_cusparselt_cu12-0.7.1-py3-none-win_amd64.whl", hash = "sha256:f67fbb5831940ec829c9117b7f33807db9f9678dc2a617fbe781cac17b4e1075"},
+]
+
+[[package]]
+name = "nvidia-ml-py"
+version = "12.575.51"
+description = "Python Bindings for the NVIDIA Management Library"
+optional = false
+python-versions = "*"
+groups = ["main"]
+files = [
+ {file = "nvidia_ml_py-12.575.51-py3-none-any.whl", hash = "sha256:eb8641800d98ce40a22f479873f34b482e214a7e80349c63be51c3919845446e"},
+ {file = "nvidia_ml_py-12.575.51.tar.gz", hash = "sha256:6490e93fea99eb4e966327ae18c6eec6256194c921f23459c8767aee28c54581"},
+]
+
+[[package]]
+name = "nvidia-nccl-cu12"
+version = "2.26.2"
+description = "NVIDIA Collective Communication Library (NCCL) Runtime"
+optional = false
+python-versions = ">=3"
+groups = ["main"]
+markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version >= \"3.12\""
+files = [
+ {file = "nvidia_nccl_cu12-2.26.2-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5c196e95e832ad30fbbb50381eb3cbd1fadd5675e587a548563993609af19522"},
+ {file = "nvidia_nccl_cu12-2.26.2-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:694cf3879a206553cc9d7dbda76b13efaf610fdb70a50cba303de1b0d1530ac6"},
+]
+
+[[package]]
+name = "nvidia-nccl-cu12"
+version = "2.27.3"
+description = "NVIDIA Collective Communication Library (NCCL) Runtime"
+optional = false
+python-versions = ">=3"
+groups = ["main"]
+markers = "platform_machine == \"x86_64\" and platform_system == \"Linux\" and python_version <= \"3.11\""
+files = [
+ {file = "nvidia_nccl_cu12-2.27.3-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:9ddf1a245abc36c550870f26d537a9b6087fb2e2e3d6e0ef03374c6fd19d984f"},
+ {file = "nvidia_nccl_cu12-2.27.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:adf27ccf4238253e0b826bce3ff5fa532d65fc42322c8bfdfaf28024c0fbe039"},
+]
+
+[[package]]
+name = "nvidia-nvjitlink-cu12"
+version = "12.6.85"
+description = "Nvidia JIT LTO Library"
+optional = false
+python-versions = ">=3"
+groups = ["main"]
+markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version >= \"3.12\""
+files = [
+ {file = "nvidia_nvjitlink_cu12-12.6.85-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:eedc36df9e88b682efe4309aa16b5b4e78c2407eac59e8c10a6a47535164369a"},
+ {file = "nvidia_nvjitlink_cu12-12.6.85-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cf4eaa7d4b6b543ffd69d6abfb11efdeb2db48270d94dfd3a452c24150829e41"},
+ {file = "nvidia_nvjitlink_cu12-12.6.85-py3-none-win_amd64.whl", hash = "sha256:e61120e52ed675747825cdd16febc6a0730537451d867ee58bee3853b1b13d1c"},
+]
+
+[[package]]
+name = "nvidia-nvjitlink-cu12"
+version = "12.8.93"
+description = "Nvidia JIT LTO Library"
+optional = false
+python-versions = ">=3"
+groups = ["main"]
+markers = "platform_machine == \"x86_64\" and platform_system == \"Linux\" and python_version <= \"3.11\""
+files = [
+ {file = "nvidia_nvjitlink_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:81ff63371a7ebd6e6451970684f916be2eab07321b73c9d244dc2b4da7f73b88"},
+ {file = "nvidia_nvjitlink_cu12-12.8.93-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:adccd7161ace7261e01bb91e44e88da350895c270d23f744f0820c818b7229e7"},
+ {file = "nvidia_nvjitlink_cu12-12.8.93-py3-none-win_amd64.whl", hash = "sha256:bd93fbeeee850917903583587f4fc3a4eafa022e34572251368238ab5e6bd67f"},
+]
+
+[[package]]
+name = "nvidia-nvtx-cu12"
+version = "12.6.77"
+description = "NVIDIA Tools Extension"
+optional = false
+python-versions = ">=3"
+groups = ["main"]
+markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version >= \"3.12\""
+files = [
+ {file = "nvidia_nvtx_cu12-12.6.77-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f44f8d86bb7d5629988d61c8d3ae61dddb2015dee142740536bc7481b022fe4b"},
+ {file = "nvidia_nvtx_cu12-12.6.77-py3-none-manylinux2014_aarch64.whl", hash = "sha256:adcaabb9d436c9761fca2b13959a2d237c5f9fd406c8e4b723c695409ff88059"},
+ {file = "nvidia_nvtx_cu12-12.6.77-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b90bed3df379fa79afbd21be8e04a0314336b8ae16768b58f2d34cb1d04cd7d2"},
+ {file = "nvidia_nvtx_cu12-12.6.77-py3-none-manylinux2014_x86_64.whl", hash = "sha256:6574241a3ec5fdc9334353ab8c479fe75841dbe8f4532a8fc97ce63503330ba1"},
+ {file = "nvidia_nvtx_cu12-12.6.77-py3-none-win_amd64.whl", hash = "sha256:2fb11a4af04a5e6c84073e6404d26588a34afd35379f0855a99797897efa75c0"},
+]
+
+[[package]]
+name = "nvidia-nvtx-cu12"
+version = "12.8.90"
+description = "NVIDIA Tools Extension"
+optional = false
+python-versions = ">=3"
+groups = ["main"]
+markers = "platform_machine == \"x86_64\" and platform_system == \"Linux\" and python_version <= \"3.11\""
+files = [
+ {file = "nvidia_nvtx_cu12-12.8.90-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d7ad891da111ebafbf7e015d34879f7112832fc239ff0d7d776b6cb685274615"},
+ {file = "nvidia_nvtx_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5b17e2001cc0d751a5bc2c6ec6d26ad95913324a4adb86788c944f8ce9ba441f"},
+ {file = "nvidia_nvtx_cu12-12.8.90-py3-none-win_amd64.whl", hash = "sha256:619c8304aedc69f02ea82dd244541a83c3d9d40993381b3b590f1adaed3db41e"},
+]
+
+[[package]]
+name = "packaging"
+version = "25.0"
+description = "Core utilities for Python packages"
+optional = false
+python-versions = ">=3.8"
+groups = ["main", "benchmarks", "dev", "docs"]
+files = [
+ {file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"},
+ {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"},
+]
+
+[[package]]
+name = "pandas"
+version = "2.3.2"
+description = "Powerful data structures for data analysis, time series, and statistics"
+optional = false
+python-versions = ">=3.9"
+groups = ["main"]
+files = [
+ {file = "pandas-2.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:52bc29a946304c360561974c6542d1dd628ddafa69134a7131fdfd6a5d7a1a35"},
+ {file = "pandas-2.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:220cc5c35ffaa764dd5bb17cf42df283b5cb7fdf49e10a7b053a06c9cb48ee2b"},
+ {file = "pandas-2.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42c05e15111221384019897df20c6fe893b2f697d03c811ee67ec9e0bb5a3424"},
+ {file = "pandas-2.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc03acc273c5515ab69f898df99d9d4f12c4d70dbfc24c3acc6203751d0804cf"},
+ {file = "pandas-2.3.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d25c20a03e8870f6339bcf67281b946bd20b86f1a544ebbebb87e66a8d642cba"},
+ {file = "pandas-2.3.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:21bb612d148bb5860b7eb2c10faacf1a810799245afd342cf297d7551513fbb6"},
+ {file = "pandas-2.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:b62d586eb25cb8cb70a5746a378fc3194cb7f11ea77170d59f889f5dfe3cec7a"},
+ {file = "pandas-2.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1333e9c299adcbb68ee89a9bb568fc3f20f9cbb419f1dd5225071e6cddb2a743"},
+ {file = "pandas-2.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:76972bcbd7de8e91ad5f0ca884a9f2c477a2125354af624e022c49e5bd0dfff4"},
+ {file = "pandas-2.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b98bdd7c456a05eef7cd21fd6b29e3ca243591fe531c62be94a2cc987efb5ac2"},
+ {file = "pandas-2.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1d81573b3f7db40d020983f78721e9bfc425f411e616ef019a10ebf597aedb2e"},
+ {file = "pandas-2.3.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e190b738675a73b581736cc8ec71ae113d6c3768d0bd18bffa5b9a0927b0b6ea"},
+ {file = "pandas-2.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c253828cb08f47488d60f43c5fc95114c771bbfff085da54bfc79cb4f9e3a372"},
+ {file = "pandas-2.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:9467697b8083f9667b212633ad6aa4ab32436dcbaf4cd57325debb0ddef2012f"},
+ {file = "pandas-2.3.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3fbb977f802156e7a3f829e9d1d5398f6192375a3e2d1a9ee0803e35fe70a2b9"},
+ {file = "pandas-2.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1b9b52693123dd234b7c985c68b709b0b009f4521000d0525f2b95c22f15944b"},
+ {file = "pandas-2.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bd281310d4f412733f319a5bc552f86d62cddc5f51d2e392c8787335c994175"},
+ {file = "pandas-2.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96d31a6b4354e3b9b8a2c848af75d31da390657e3ac6f30c05c82068b9ed79b9"},
+ {file = "pandas-2.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:df4df0b9d02bb873a106971bb85d448378ef14b86ba96f035f50bbd3688456b4"},
+ {file = "pandas-2.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:213a5adf93d020b74327cb2c1b842884dbdd37f895f42dcc2f09d451d949f811"},
+ {file = "pandas-2.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:8c13b81a9347eb8c7548f53fd9a4f08d4dfe996836543f805c987bafa03317ae"},
+ {file = "pandas-2.3.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0c6ecbac99a354a051ef21c5307601093cb9e0f4b1855984a084bfec9302699e"},
+ {file = "pandas-2.3.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c6f048aa0fd080d6a06cc7e7537c09b53be6642d330ac6f54a600c3ace857ee9"},
+ {file = "pandas-2.3.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0064187b80a5be6f2f9c9d6bdde29372468751dfa89f4211a3c5871854cfbf7a"},
+ {file = "pandas-2.3.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ac8c320bded4718b298281339c1a50fb00a6ba78cb2a63521c39bec95b0209b"},
+ {file = "pandas-2.3.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:114c2fe4f4328cf98ce5716d1532f3ab79c5919f95a9cfee81d9140064a2e4d6"},
+ {file = "pandas-2.3.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:48fa91c4dfb3b2b9bfdb5c24cd3567575f4e13f9636810462ffed8925352be5a"},
+ {file = "pandas-2.3.2-cp313-cp313-win_amd64.whl", hash = "sha256:12d039facec710f7ba305786837d0225a3444af7bbd9c15c32ca2d40d157ed8b"},
+ {file = "pandas-2.3.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:c624b615ce97864eb588779ed4046186f967374185c047070545253a52ab2d57"},
+ {file = "pandas-2.3.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:0cee69d583b9b128823d9514171cabb6861e09409af805b54459bd0c821a35c2"},
+ {file = "pandas-2.3.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2319656ed81124982900b4c37f0e0c58c015af9a7bbc62342ba5ad07ace82ba9"},
+ {file = "pandas-2.3.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b37205ad6f00d52f16b6d09f406434ba928c1a1966e2771006a9033c736d30d2"},
+ {file = "pandas-2.3.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:837248b4fc3a9b83b9c6214699a13f069dc13510a6a6d7f9ba33145d2841a012"},
+ {file = "pandas-2.3.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d2c3554bd31b731cd6490d94a28f3abb8dd770634a9e06eb6d2911b9827db370"},
+ {file = "pandas-2.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:88080a0ff8a55eac9c84e3ff3c7665b3b5476c6fbc484775ca1910ce1c3e0b87"},
+ {file = "pandas-2.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d4a558c7620340a0931828d8065688b3cc5b4c8eb674bcaf33d18ff4a6870b4a"},
+ {file = "pandas-2.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45178cf09d1858a1509dc73ec261bf5b25a625a389b65be2e47b559905f0ab6a"},
+ {file = "pandas-2.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77cefe00e1b210f9c76c697fedd8fdb8d3dd86563e9c8adc9fa72b90f5e9e4c2"},
+ {file = "pandas-2.3.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:13bd629c653856f00c53dc495191baa59bcafbbf54860a46ecc50d3a88421a96"},
+ {file = "pandas-2.3.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:36d627906fd44b5fd63c943264e11e96e923f8de77d6016dc2f667b9ad193438"},
+ {file = "pandas-2.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:a9d7ec92d71a420185dec44909c32e9a362248c4ae2238234b76d5be37f208cc"},
+ {file = "pandas-2.3.2.tar.gz", hash = "sha256:ab7b58f8f82706890924ccdfb5f48002b83d2b5a3845976a9fb705d36c34dcdb"},
+]
+
+[package.dependencies]
+numpy = [
+ {version = ">=1.22.4", markers = "python_version < \"3.11\""},
+ {version = ">=1.23.2", markers = "python_version == \"3.11\""},
+ {version = ">=1.26.0", markers = "python_version >= \"3.12\""},
+]
+python-dateutil = ">=2.8.2"
+pytz = ">=2020.1"
+tzdata = ">=2022.7"
+
+[package.extras]
+all = ["PyQt5 (>=5.15.9)", "SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "adbc-driver-sqlite (>=0.8.0)", "beautifulsoup4 (>=4.11.2)", "bottleneck (>=1.3.6)", "dataframe-api-compat (>=0.1.7)", "fastparquet (>=2022.12.0)", "fsspec (>=2022.11.0)", "gcsfs (>=2022.11.0)", "html5lib (>=1.1)", "hypothesis (>=6.46.1)", "jinja2 (>=3.1.2)", "lxml (>=4.9.2)", "matplotlib (>=3.6.3)", "numba (>=0.56.4)", "numexpr (>=2.8.4)", "odfpy (>=1.4.1)", "openpyxl (>=3.1.0)", "pandas-gbq (>=0.19.0)", "psycopg2 (>=2.9.6)", "pyarrow (>=10.0.1)", "pymysql (>=1.0.2)", "pyreadstat (>=1.2.0)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)", "python-calamine (>=0.1.7)", "pyxlsb (>=1.0.10)", "qtpy (>=2.3.0)", "s3fs (>=2022.11.0)", "scipy (>=1.10.0)", "tables (>=3.8.0)", "tabulate (>=0.9.0)", "xarray (>=2022.12.0)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.5)", "zstandard (>=0.19.0)"]
+aws = ["s3fs (>=2022.11.0)"]
+clipboard = ["PyQt5 (>=5.15.9)", "qtpy (>=2.3.0)"]
+compression = ["zstandard (>=0.19.0)"]
+computation = ["scipy (>=1.10.0)", "xarray (>=2022.12.0)"]
+consortium-standard = ["dataframe-api-compat (>=0.1.7)"]
+excel = ["odfpy (>=1.4.1)", "openpyxl (>=3.1.0)", "python-calamine (>=0.1.7)", "pyxlsb (>=1.0.10)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.5)"]
+feather = ["pyarrow (>=10.0.1)"]
+fss = ["fsspec (>=2022.11.0)"]
+gcp = ["gcsfs (>=2022.11.0)", "pandas-gbq (>=0.19.0)"]
+hdf5 = ["tables (>=3.8.0)"]
+html = ["beautifulsoup4 (>=4.11.2)", "html5lib (>=1.1)", "lxml (>=4.9.2)"]
+mysql = ["SQLAlchemy (>=2.0.0)", "pymysql (>=1.0.2)"]
+output-formatting = ["jinja2 (>=3.1.2)", "tabulate (>=0.9.0)"]
+parquet = ["pyarrow (>=10.0.1)"]
+performance = ["bottleneck (>=1.3.6)", "numba (>=0.56.4)", "numexpr (>=2.8.4)"]
+plot = ["matplotlib (>=3.6.3)"]
+postgresql = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "psycopg2 (>=2.9.6)"]
+pyarrow = ["pyarrow (>=10.0.1)"]
+spss = ["pyreadstat (>=1.2.0)"]
+sql-other = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "adbc-driver-sqlite (>=0.8.0)"]
+test = ["hypothesis (>=6.46.1)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)"]
+xml = ["lxml (>=4.9.2)"]
+
+[[package]]
+name = "pathspec"
+version = "0.12.1"
+description = "Utility library for gitignore style pattern matching of file paths."
+optional = false
+python-versions = ">=3.8"
+groups = ["dev"]
+files = [
+ {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"},
+ {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"},
+]
+
+[[package]]
+name = "pillow"
+version = "11.3.0"
+description = "Python Imaging Library (Fork)"
+optional = false
+python-versions = ">=3.9"
+groups = ["main"]
+files = [
+ {file = "pillow-11.3.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:1b9c17fd4ace828b3003dfd1e30bff24863e0eb59b535e8f80194d9cc7ecf860"},
+ {file = "pillow-11.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:65dc69160114cdd0ca0f35cb434633c75e8e7fad4cf855177a05bf38678f73ad"},
+ {file = "pillow-11.3.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7107195ddc914f656c7fc8e4a5e1c25f32e9236ea3ea860f257b0436011fddd0"},
+ {file = "pillow-11.3.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cc3e831b563b3114baac7ec2ee86819eb03caa1a2cef0b481a5675b59c4fe23b"},
+ {file = "pillow-11.3.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f1f182ebd2303acf8c380a54f615ec883322593320a9b00438eb842c1f37ae50"},
+ {file = "pillow-11.3.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4445fa62e15936a028672fd48c4c11a66d641d2c05726c7ec1f8ba6a572036ae"},
+ {file = "pillow-11.3.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:71f511f6b3b91dd543282477be45a033e4845a40278fa8dcdbfdb07109bf18f9"},
+ {file = "pillow-11.3.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:040a5b691b0713e1f6cbe222e0f4f74cd233421e105850ae3b3c0ceda520f42e"},
+ {file = "pillow-11.3.0-cp310-cp310-win32.whl", hash = "sha256:89bd777bc6624fe4115e9fac3352c79ed60f3bb18651420635f26e643e3dd1f6"},
+ {file = "pillow-11.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:19d2ff547c75b8e3ff46f4d9ef969a06c30ab2d4263a9e287733aa8b2429ce8f"},
+ {file = "pillow-11.3.0-cp310-cp310-win_arm64.whl", hash = "sha256:819931d25e57b513242859ce1876c58c59dc31587847bf74cfe06b2e0cb22d2f"},
+ {file = "pillow-11.3.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:1cd110edf822773368b396281a2293aeb91c90a2db00d78ea43e7e861631b722"},
+ {file = "pillow-11.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9c412fddd1b77a75aa904615ebaa6001f169b26fd467b4be93aded278266b288"},
+ {file = "pillow-11.3.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7d1aa4de119a0ecac0a34a9c8bde33f34022e2e8f99104e47a3ca392fd60e37d"},
+ {file = "pillow-11.3.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:91da1d88226663594e3f6b4b8c3c8d85bd504117d043740a8e0ec449087cc494"},
+ {file = "pillow-11.3.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:643f189248837533073c405ec2f0bb250ba54598cf80e8c1e043381a60632f58"},
+ {file = "pillow-11.3.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:106064daa23a745510dabce1d84f29137a37224831d88eb4ce94bb187b1d7e5f"},
+ {file = "pillow-11.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cd8ff254faf15591e724dc7c4ddb6bf4793efcbe13802a4ae3e863cd300b493e"},
+ {file = "pillow-11.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:932c754c2d51ad2b2271fd01c3d121daaa35e27efae2a616f77bf164bc0b3e94"},
+ {file = "pillow-11.3.0-cp311-cp311-win32.whl", hash = "sha256:b4b8f3efc8d530a1544e5962bd6b403d5f7fe8b9e08227c6b255f98ad82b4ba0"},
+ {file = "pillow-11.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:1a992e86b0dd7aeb1f053cd506508c0999d710a8f07b4c791c63843fc6a807ac"},
+ {file = "pillow-11.3.0-cp311-cp311-win_arm64.whl", hash = "sha256:30807c931ff7c095620fe04448e2c2fc673fcbb1ffe2a7da3fb39613489b1ddd"},
+ {file = "pillow-11.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fdae223722da47b024b867c1ea0be64e0df702c5e0a60e27daad39bf960dd1e4"},
+ {file = "pillow-11.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:921bd305b10e82b4d1f5e802b6850677f965d8394203d182f078873851dada69"},
+ {file = "pillow-11.3.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:eb76541cba2f958032d79d143b98a3a6b3ea87f0959bbe256c0b5e416599fd5d"},
+ {file = "pillow-11.3.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:67172f2944ebba3d4a7b54f2e95c786a3a50c21b88456329314caaa28cda70f6"},
+ {file = "pillow-11.3.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97f07ed9f56a3b9b5f49d3661dc9607484e85c67e27f3e8be2c7d28ca032fec7"},
+ {file = "pillow-11.3.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:676b2815362456b5b3216b4fd5bd89d362100dc6f4945154ff172e206a22c024"},
+ {file = "pillow-11.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3e184b2f26ff146363dd07bde8b711833d7b0202e27d13540bfe2e35a323a809"},
+ {file = "pillow-11.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6be31e3fc9a621e071bc17bb7de63b85cbe0bfae91bb0363c893cbe67247780d"},
+ {file = "pillow-11.3.0-cp312-cp312-win32.whl", hash = "sha256:7b161756381f0918e05e7cb8a371fff367e807770f8fe92ecb20d905d0e1c149"},
+ {file = "pillow-11.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:a6444696fce635783440b7f7a9fc24b3ad10a9ea3f0ab66c5905be1c19ccf17d"},
+ {file = "pillow-11.3.0-cp312-cp312-win_arm64.whl", hash = "sha256:2aceea54f957dd4448264f9bf40875da0415c83eb85f55069d89c0ed436e3542"},
+ {file = "pillow-11.3.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:1c627742b539bba4309df89171356fcb3cc5a9178355b2727d1b74a6cf155fbd"},
+ {file = "pillow-11.3.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:30b7c02f3899d10f13d7a48163c8969e4e653f8b43416d23d13d1bbfdc93b9f8"},
+ {file = "pillow-11.3.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:7859a4cc7c9295f5838015d8cc0a9c215b77e43d07a25e460f35cf516df8626f"},
+ {file = "pillow-11.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ec1ee50470b0d050984394423d96325b744d55c701a439d2bd66089bff963d3c"},
+ {file = "pillow-11.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7db51d222548ccfd274e4572fdbf3e810a5e66b00608862f947b163e613b67dd"},
+ {file = "pillow-11.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2d6fcc902a24ac74495df63faad1884282239265c6839a0a6416d33faedfae7e"},
+ {file = "pillow-11.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f0f5d8f4a08090c6d6d578351a2b91acf519a54986c055af27e7a93feae6d3f1"},
+ {file = "pillow-11.3.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c37d8ba9411d6003bba9e518db0db0c58a680ab9fe5179f040b0463644bc9805"},
+ {file = "pillow-11.3.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:13f87d581e71d9189ab21fe0efb5a23e9f28552d5be6979e84001d3b8505abe8"},
+ {file = "pillow-11.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:023f6d2d11784a465f09fd09a34b150ea4672e85fb3d05931d89f373ab14abb2"},
+ {file = "pillow-11.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:45dfc51ac5975b938e9809451c51734124e73b04d0f0ac621649821a63852e7b"},
+ {file = "pillow-11.3.0-cp313-cp313-win32.whl", hash = "sha256:a4d336baed65d50d37b88ca5b60c0fa9d81e3a87d4a7930d3880d1624d5b31f3"},
+ {file = "pillow-11.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:0bce5c4fd0921f99d2e858dc4d4d64193407e1b99478bc5cacecba2311abde51"},
+ {file = "pillow-11.3.0-cp313-cp313-win_arm64.whl", hash = "sha256:1904e1264881f682f02b7f8167935cce37bc97db457f8e7849dc3a6a52b99580"},
+ {file = "pillow-11.3.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:4c834a3921375c48ee6b9624061076bc0a32a60b5532b322cc0ea64e639dd50e"},
+ {file = "pillow-11.3.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5e05688ccef30ea69b9317a9ead994b93975104a677a36a8ed8106be9260aa6d"},
+ {file = "pillow-11.3.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1019b04af07fc0163e2810167918cb5add8d74674b6267616021ab558dc98ced"},
+ {file = "pillow-11.3.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f944255db153ebb2b19c51fe85dd99ef0ce494123f21b9db4877ffdfc5590c7c"},
+ {file = "pillow-11.3.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1f85acb69adf2aaee8b7da124efebbdb959a104db34d3a2cb0f3793dbae422a8"},
+ {file = "pillow-11.3.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:05f6ecbeff5005399bb48d198f098a9b4b6bdf27b8487c7f38ca16eeb070cd59"},
+ {file = "pillow-11.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a7bc6e6fd0395bc052f16b1a8670859964dbd7003bd0af2ff08342eb6e442cfe"},
+ {file = "pillow-11.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:83e1b0161c9d148125083a35c1c5a89db5b7054834fd4387499e06552035236c"},
+ {file = "pillow-11.3.0-cp313-cp313t-win32.whl", hash = "sha256:2a3117c06b8fb646639dce83694f2f9eac405472713fcb1ae887469c0d4f6788"},
+ {file = "pillow-11.3.0-cp313-cp313t-win_amd64.whl", hash = "sha256:857844335c95bea93fb39e0fa2726b4d9d758850b34075a7e3ff4f4fa3aa3b31"},
+ {file = "pillow-11.3.0-cp313-cp313t-win_arm64.whl", hash = "sha256:8797edc41f3e8536ae4b10897ee2f637235c94f27404cac7297f7b607dd0716e"},
+ {file = "pillow-11.3.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:d9da3df5f9ea2a89b81bb6087177fb1f4d1c7146d583a3fe5c672c0d94e55e12"},
+ {file = "pillow-11.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0b275ff9b04df7b640c59ec5a3cb113eefd3795a8df80bac69646ef699c6981a"},
+ {file = "pillow-11.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0743841cabd3dba6a83f38a92672cccbd69af56e3e91777b0ee7f4dba4385632"},
+ {file = "pillow-11.3.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2465a69cf967b8b49ee1b96d76718cd98c4e925414ead59fdf75cf0fd07df673"},
+ {file = "pillow-11.3.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:41742638139424703b4d01665b807c6468e23e699e8e90cffefe291c5832b027"},
+ {file = "pillow-11.3.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:93efb0b4de7e340d99057415c749175e24c8864302369e05914682ba642e5d77"},
+ {file = "pillow-11.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7966e38dcd0fa11ca390aed7c6f20454443581d758242023cf36fcb319b1a874"},
+ {file = "pillow-11.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:98a9afa7b9007c67ed84c57c9e0ad86a6000da96eaa638e4f8abe5b65ff83f0a"},
+ {file = "pillow-11.3.0-cp314-cp314-win32.whl", hash = "sha256:02a723e6bf909e7cea0dac1b0e0310be9d7650cd66222a5f1c571455c0a45214"},
+ {file = "pillow-11.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:a418486160228f64dd9e9efcd132679b7a02a5f22c982c78b6fc7dab3fefb635"},
+ {file = "pillow-11.3.0-cp314-cp314-win_arm64.whl", hash = "sha256:155658efb5e044669c08896c0c44231c5e9abcaadbc5cd3648df2f7c0b96b9a6"},
+ {file = "pillow-11.3.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:59a03cdf019efbfeeed910bf79c7c93255c3d54bc45898ac2a4140071b02b4ae"},
+ {file = "pillow-11.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f8a5827f84d973d8636e9dc5764af4f0cf2318d26744b3d902931701b0d46653"},
+ {file = "pillow-11.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ee92f2fd10f4adc4b43d07ec5e779932b4eb3dbfbc34790ada5a6669bc095aa6"},
+ {file = "pillow-11.3.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c96d333dcf42d01f47b37e0979b6bd73ec91eae18614864622d9b87bbd5bbf36"},
+ {file = "pillow-11.3.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4c96f993ab8c98460cd0c001447bff6194403e8b1d7e149ade5f00594918128b"},
+ {file = "pillow-11.3.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:41342b64afeba938edb034d122b2dda5db2139b9a4af999729ba8818e0056477"},
+ {file = "pillow-11.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:068d9c39a2d1b358eb9f245ce7ab1b5c3246c7c8c7d9ba58cfa5b43146c06e50"},
+ {file = "pillow-11.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a1bc6ba083b145187f648b667e05a2534ecc4b9f2784c2cbe3089e44868f2b9b"},
+ {file = "pillow-11.3.0-cp314-cp314t-win32.whl", hash = "sha256:118ca10c0d60b06d006be10a501fd6bbdfef559251ed31b794668ed569c87e12"},
+ {file = "pillow-11.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:8924748b688aa210d79883357d102cd64690e56b923a186f35a82cbc10f997db"},
+ {file = "pillow-11.3.0-cp314-cp314t-win_arm64.whl", hash = "sha256:79ea0d14d3ebad43ec77ad5272e6ff9bba5b679ef73375ea760261207fa8e0aa"},
+ {file = "pillow-11.3.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:48d254f8a4c776de343051023eb61ffe818299eeac478da55227d96e241de53f"},
+ {file = "pillow-11.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7aee118e30a4cf54fdd873bd3a29de51e29105ab11f9aad8c32123f58c8f8081"},
+ {file = "pillow-11.3.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:23cff760a9049c502721bdb743a7cb3e03365fafcdfc2ef9784610714166e5a4"},
+ {file = "pillow-11.3.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6359a3bc43f57d5b375d1ad54a0074318a0844d11b76abccf478c37c986d3cfc"},
+ {file = "pillow-11.3.0-cp39-cp39-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:092c80c76635f5ecb10f3f83d76716165c96f5229addbd1ec2bdbbda7d496e06"},
+ {file = "pillow-11.3.0-cp39-cp39-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cadc9e0ea0a2431124cde7e1697106471fc4c1da01530e679b2391c37d3fbb3a"},
+ {file = "pillow-11.3.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:6a418691000f2a418c9135a7cf0d797c1bb7d9a485e61fe8e7722845b95ef978"},
+ {file = "pillow-11.3.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:97afb3a00b65cc0804d1c7abddbf090a81eaac02768af58cbdcaaa0a931e0b6d"},
+ {file = "pillow-11.3.0-cp39-cp39-win32.whl", hash = "sha256:ea944117a7974ae78059fcc1800e5d3295172bb97035c0c1d9345fca1419da71"},
+ {file = "pillow-11.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:e5c5858ad8ec655450a7c7df532e9842cf8df7cc349df7225c60d5d348c8aada"},
+ {file = "pillow-11.3.0-cp39-cp39-win_arm64.whl", hash = "sha256:6abdbfd3aea42be05702a8dd98832329c167ee84400a1d1f61ab11437f1717eb"},
+ {file = "pillow-11.3.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:3cee80663f29e3843b68199b9d6f4f54bd1d4a6b59bdd91bceefc51238bcb967"},
+ {file = "pillow-11.3.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:b5f56c3f344f2ccaf0dd875d3e180f631dc60a51b314295a3e681fe8cf851fbe"},
+ {file = "pillow-11.3.0-pp310-pypy310_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e67d793d180c9df62f1f40aee3accca4829d3794c95098887edc18af4b8b780c"},
+ {file = "pillow-11.3.0-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d000f46e2917c705e9fb93a3606ee4a819d1e3aa7a9b442f6444f07e77cf5e25"},
+ {file = "pillow-11.3.0-pp310-pypy310_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:527b37216b6ac3a12d7838dc3bd75208ec57c1c6d11ef01902266a5a0c14fc27"},
+ {file = "pillow-11.3.0-pp310-pypy310_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:be5463ac478b623b9dd3937afd7fb7ab3d79dd290a28e2b6df292dc75063eb8a"},
+ {file = "pillow-11.3.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:8dc70ca24c110503e16918a658b869019126ecfe03109b754c402daff12b3d9f"},
+ {file = "pillow-11.3.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7c8ec7a017ad1bd562f93dbd8505763e688d388cde6e4a010ae1486916e713e6"},
+ {file = "pillow-11.3.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:9ab6ae226de48019caa8074894544af5b53a117ccb9d3b3dcb2871464c829438"},
+ {file = "pillow-11.3.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fe27fb049cdcca11f11a7bfda64043c37b30e6b91f10cb5bab275806c32f6ab3"},
+ {file = "pillow-11.3.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:465b9e8844e3c3519a983d58b80be3f668e2a7a5db97f2784e7079fbc9f9822c"},
+ {file = "pillow-11.3.0-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5418b53c0d59b3824d05e029669efa023bbef0f3e92e75ec8428f3799487f361"},
+ {file = "pillow-11.3.0-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:504b6f59505f08ae014f724b6207ff6222662aab5cc9542577fb084ed0676ac7"},
+ {file = "pillow-11.3.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:c84d689db21a1c397d001aa08241044aa2069e7587b398c8cc63020390b1c1b8"},
+ {file = "pillow-11.3.0.tar.gz", hash = "sha256:3828ee7586cd0b2091b6209e5ad53e20d0649bbe87164a459d0676e035e8f523"},
+]
+
+[package.extras]
+docs = ["furo", "olefile", "sphinx (>=8.2)", "sphinx-autobuild", "sphinx-copybutton", "sphinx-inline-tabs", "sphinxext-opengraph"]
+fpx = ["olefile"]
+mic = ["olefile"]
+test-arrow = ["pyarrow"]
+tests = ["check-manifest", "coverage (>=7.4.2)", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "trove-classifiers (>=2024.10.12)"]
+typing = ["typing-extensions ; python_version < \"3.10\""]
+xmp = ["defusedxml"]
+
+[[package]]
+name = "platformdirs"
+version = "4.4.0"
+description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`."
+optional = false
+python-versions = ">=3.9"
+groups = ["benchmarks", "dev"]
+files = [
+ {file = "platformdirs-4.4.0-py3-none-any.whl", hash = "sha256:abd01743f24e5287cd7a5db3752faf1a2d65353f38ec26d98e25a6db65958c85"},
+ {file = "platformdirs-4.4.0.tar.gz", hash = "sha256:ca753cf4d81dc309bc67b0ea38fd15dc97bc30ce419a7f58d13eb3bf14c4febf"},
+]
+
+[package.extras]
+docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.1.3)", "sphinx-autodoc-typehints (>=3)"]
+test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.4)", "pytest-cov (>=6)", "pytest-mock (>=3.14)"]
+type = ["mypy (>=1.14.1)"]
+
+[[package]]
+name = "plotly"
+version = "6.3.0"
+description = "An open-source interactive data visualization library for Python"
+optional = false
+python-versions = ">=3.8"
+groups = ["main"]
+files = [
+ {file = "plotly-6.3.0-py3-none-any.whl", hash = "sha256:7ad806edce9d3cdd882eaebaf97c0c9e252043ed1ed3d382c3e3520ec07806d4"},
+ {file = "plotly-6.3.0.tar.gz", hash = "sha256:8840a184d18ccae0f9189c2b9a2943923fd5cae7717b723f36eef78f444e5a73"},
+]
+
+[package.dependencies]
+narwhals = ">=1.15.1"
+packaging = "*"
+
+[package.extras]
+dev = ["plotly[dev-optional]"]
+dev-build = ["build", "jupyter", "plotly[dev-core]"]
+dev-core = ["pytest", "requests", "ruff (==0.11.12)"]
+dev-optional = ["anywidget", "colorcet", "fiona (<=1.9.6) ; python_version <= \"3.8\"", "geopandas", "inflect", "numpy", "orjson", "pandas", "pdfrw", "pillow", "plotly-geo", "plotly[dev-build]", "plotly[kaleido]", "polars[timezone]", "pyarrow", "pyshp", "pytz", "scikit-image", "scipy", "shapely", "statsmodels", "vaex ; python_version <= \"3.9\"", "xarray"]
+express = ["numpy"]
+kaleido = ["kaleido (>=1.0.0)"]
+
+[[package]]
+name = "pluggy"
+version = "1.6.0"
+description = "plugin and hook calling mechanisms for python"
+optional = false
+python-versions = ">=3.9"
+groups = ["dev"]
+files = [
+ {file = "pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746"},
+ {file = "pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3"},
+]
+
+[package.extras]
+dev = ["pre-commit", "tox"]
+testing = ["coverage", "pytest", "pytest-benchmark"]
+
+[[package]]
+name = "pre-commit"
+version = "4.3.0"
+description = "A framework for managing and maintaining multi-language pre-commit hooks."
+optional = false
+python-versions = ">=3.9"
+groups = ["dev"]
+files = [
+ {file = "pre_commit-4.3.0-py2.py3-none-any.whl", hash = "sha256:2b0747ad7e6e967169136edffee14c16e148a778a54e4f967921aa1ebf2308d8"},
+ {file = "pre_commit-4.3.0.tar.gz", hash = "sha256:499fe450cc9d42e9d58e606262795ecb64dd05438943c62b66f6a8673da30b16"},
+]
+
+[package.dependencies]
+cfgv = ">=2.0.0"
+identify = ">=1.0.0"
+nodeenv = ">=0.11.1"
+pyyaml = ">=5.1"
+virtualenv = ">=20.10.0"
+
+[[package]]
+name = "propcache"
+version = "0.3.2"
+description = "Accelerated property cache"
+optional = false
+python-versions = ">=3.9"
+groups = ["main"]
+files = [
+ {file = "propcache-0.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:22d9962a358aedbb7a2e36187ff273adeaab9743373a272976d2e348d08c7770"},
+ {file = "propcache-0.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0d0fda578d1dc3f77b6b5a5dce3b9ad69a8250a891760a548df850a5e8da87f3"},
+ {file = "propcache-0.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3def3da3ac3ce41562d85db655d18ebac740cb3fa4367f11a52b3da9d03a5cc3"},
+ {file = "propcache-0.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9bec58347a5a6cebf239daba9bda37dffec5b8d2ce004d9fe4edef3d2815137e"},
+ {file = "propcache-0.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55ffda449a507e9fbd4aca1a7d9aa6753b07d6166140e5a18d2ac9bc49eac220"},
+ {file = "propcache-0.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:64a67fb39229a8a8491dd42f864e5e263155e729c2e7ff723d6e25f596b1e8cb"},
+ {file = "propcache-0.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9da1cf97b92b51253d5b68cf5a2b9e0dafca095e36b7f2da335e27dc6172a614"},
+ {file = "propcache-0.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5f559e127134b07425134b4065be45b166183fdcb433cb6c24c8e4149056ad50"},
+ {file = "propcache-0.3.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:aff2e4e06435d61f11a428360a932138d0ec288b0a31dd9bd78d200bd4a2b339"},
+ {file = "propcache-0.3.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:4927842833830942a5d0a56e6f4839bc484785b8e1ce8d287359794818633ba0"},
+ {file = "propcache-0.3.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:6107ddd08b02654a30fb8ad7a132021759d750a82578b94cd55ee2772b6ebea2"},
+ {file = "propcache-0.3.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:70bd8b9cd6b519e12859c99f3fc9a93f375ebd22a50296c3a295028bea73b9e7"},
+ {file = "propcache-0.3.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2183111651d710d3097338dd1893fcf09c9f54e27ff1a8795495a16a469cc90b"},
+ {file = "propcache-0.3.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:fb075ad271405dcad8e2a7ffc9a750a3bf70e533bd86e89f0603e607b93aa64c"},
+ {file = "propcache-0.3.2-cp310-cp310-win32.whl", hash = "sha256:404d70768080d3d3bdb41d0771037da19d8340d50b08e104ca0e7f9ce55fce70"},
+ {file = "propcache-0.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:7435d766f978b4ede777002e6b3b6641dd229cd1da8d3d3106a45770365f9ad9"},
+ {file = "propcache-0.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0b8d2f607bd8f80ddc04088bc2a037fdd17884a6fcadc47a96e334d72f3717be"},
+ {file = "propcache-0.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:06766d8f34733416e2e34f46fea488ad5d60726bb9481d3cddf89a6fa2d9603f"},
+ {file = "propcache-0.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a2dc1f4a1df4fecf4e6f68013575ff4af84ef6f478fe5344317a65d38a8e6dc9"},
+ {file = "propcache-0.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be29c4f4810c5789cf10ddf6af80b041c724e629fa51e308a7a0fb19ed1ef7bf"},
+ {file = "propcache-0.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59d61f6970ecbd8ff2e9360304d5c8876a6abd4530cb752c06586849ac8a9dc9"},
+ {file = "propcache-0.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:62180e0b8dbb6b004baec00a7983e4cc52f5ada9cd11f48c3528d8cfa7b96a66"},
+ {file = "propcache-0.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c144ca294a204c470f18cf4c9d78887810d04a3e2fbb30eea903575a779159df"},
+ {file = "propcache-0.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c5c2a784234c28854878d68978265617aa6dc0780e53d44b4d67f3651a17a9a2"},
+ {file = "propcache-0.3.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5745bc7acdafa978ca1642891b82c19238eadc78ba2aaa293c6863b304e552d7"},
+ {file = "propcache-0.3.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:c0075bf773d66fa8c9d41f66cc132ecc75e5bb9dd7cce3cfd14adc5ca184cb95"},
+ {file = "propcache-0.3.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5f57aa0847730daceff0497f417c9de353c575d8da3579162cc74ac294c5369e"},
+ {file = "propcache-0.3.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:eef914c014bf72d18efb55619447e0aecd5fb7c2e3fa7441e2e5d6099bddff7e"},
+ {file = "propcache-0.3.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:2a4092e8549031e82facf3decdbc0883755d5bbcc62d3aea9d9e185549936dcf"},
+ {file = "propcache-0.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:85871b050f174bc0bfb437efbdb68aaf860611953ed12418e4361bc9c392749e"},
+ {file = "propcache-0.3.2-cp311-cp311-win32.whl", hash = "sha256:36c8d9b673ec57900c3554264e630d45980fd302458e4ac801802a7fd2ef7897"},
+ {file = "propcache-0.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:e53af8cb6a781b02d2ea079b5b853ba9430fcbe18a8e3ce647d5982a3ff69f39"},
+ {file = "propcache-0.3.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8de106b6c84506b31c27168582cd3cb3000a6412c16df14a8628e5871ff83c10"},
+ {file = "propcache-0.3.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:28710b0d3975117239c76600ea351934ac7b5ff56e60953474342608dbbb6154"},
+ {file = "propcache-0.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce26862344bdf836650ed2487c3d724b00fbfec4233a1013f597b78c1cb73615"},
+ {file = "propcache-0.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bca54bd347a253af2cf4544bbec232ab982f4868de0dd684246b67a51bc6b1db"},
+ {file = "propcache-0.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55780d5e9a2ddc59711d727226bb1ba83a22dd32f64ee15594b9392b1f544eb1"},
+ {file = "propcache-0.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:035e631be25d6975ed87ab23153db6a73426a48db688070d925aa27e996fe93c"},
+ {file = "propcache-0.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee6f22b6eaa39297c751d0e80c0d3a454f112f5c6481214fcf4c092074cecd67"},
+ {file = "propcache-0.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7ca3aee1aa955438c4dba34fc20a9f390e4c79967257d830f137bd5a8a32ed3b"},
+ {file = "propcache-0.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7a4f30862869fa2b68380d677cc1c5fcf1e0f2b9ea0cf665812895c75d0ca3b8"},
+ {file = "propcache-0.3.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:b77ec3c257d7816d9f3700013639db7491a434644c906a2578a11daf13176251"},
+ {file = "propcache-0.3.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:cab90ac9d3f14b2d5050928483d3d3b8fb6b4018893fc75710e6aa361ecb2474"},
+ {file = "propcache-0.3.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:0b504d29f3c47cf6b9e936c1852246c83d450e8e063d50562115a6be6d3a2535"},
+ {file = "propcache-0.3.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:ce2ac2675a6aa41ddb2a0c9cbff53780a617ac3d43e620f8fd77ba1c84dcfc06"},
+ {file = "propcache-0.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:62b4239611205294cc433845b914131b2a1f03500ff3c1ed093ed216b82621e1"},
+ {file = "propcache-0.3.2-cp312-cp312-win32.whl", hash = "sha256:df4a81b9b53449ebc90cc4deefb052c1dd934ba85012aa912c7ea7b7e38b60c1"},
+ {file = "propcache-0.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:7046e79b989d7fe457bb755844019e10f693752d169076138abf17f31380800c"},
+ {file = "propcache-0.3.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ca592ed634a73ca002967458187109265e980422116c0a107cf93d81f95af945"},
+ {file = "propcache-0.3.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9ecb0aad4020e275652ba3975740f241bd12a61f1a784df044cf7477a02bc252"},
+ {file = "propcache-0.3.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7f08f1cc28bd2eade7a8a3d2954ccc673bb02062e3e7da09bc75d843386b342f"},
+ {file = "propcache-0.3.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1a342c834734edb4be5ecb1e9fb48cb64b1e2320fccbd8c54bf8da8f2a84c33"},
+ {file = "propcache-0.3.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8a544caaae1ac73f1fecfae70ded3e93728831affebd017d53449e3ac052ac1e"},
+ {file = "propcache-0.3.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:310d11aa44635298397db47a3ebce7db99a4cc4b9bbdfcf6c98a60c8d5261cf1"},
+ {file = "propcache-0.3.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c1396592321ac83157ac03a2023aa6cc4a3cc3cfdecb71090054c09e5a7cce3"},
+ {file = "propcache-0.3.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8cabf5b5902272565e78197edb682017d21cf3b550ba0460ee473753f28d23c1"},
+ {file = "propcache-0.3.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0a2f2235ac46a7aa25bdeb03a9e7060f6ecbd213b1f9101c43b3090ffb971ef6"},
+ {file = "propcache-0.3.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:92b69e12e34869a6970fd2f3da91669899994b47c98f5d430b781c26f1d9f387"},
+ {file = "propcache-0.3.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:54e02207c79968ebbdffc169591009f4474dde3b4679e16634d34c9363ff56b4"},
+ {file = "propcache-0.3.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4adfb44cb588001f68c5466579d3f1157ca07f7504fc91ec87862e2b8e556b88"},
+ {file = "propcache-0.3.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:fd3e6019dc1261cd0291ee8919dd91fbab7b169bb76aeef6c716833a3f65d206"},
+ {file = "propcache-0.3.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4c181cad81158d71c41a2bce88edce078458e2dd5ffee7eddd6b05da85079f43"},
+ {file = "propcache-0.3.2-cp313-cp313-win32.whl", hash = "sha256:8a08154613f2249519e549de2330cf8e2071c2887309a7b07fb56098f5170a02"},
+ {file = "propcache-0.3.2-cp313-cp313-win_amd64.whl", hash = "sha256:e41671f1594fc4ab0a6dec1351864713cb3a279910ae8b58f884a88a0a632c05"},
+ {file = "propcache-0.3.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:9a3cf035bbaf035f109987d9d55dc90e4b0e36e04bbbb95af3055ef17194057b"},
+ {file = "propcache-0.3.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:156c03d07dc1323d8dacaa221fbe028c5c70d16709cdd63502778e6c3ccca1b0"},
+ {file = "propcache-0.3.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:74413c0ba02ba86f55cf60d18daab219f7e531620c15f1e23d95563f505efe7e"},
+ {file = "propcache-0.3.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f066b437bb3fa39c58ff97ab2ca351db465157d68ed0440abecb21715eb24b28"},
+ {file = "propcache-0.3.2-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f1304b085c83067914721e7e9d9917d41ad87696bf70f0bc7dee450e9c71ad0a"},
+ {file = "propcache-0.3.2-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ab50cef01b372763a13333b4e54021bdcb291fc9a8e2ccb9c2df98be51bcde6c"},
+ {file = "propcache-0.3.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fad3b2a085ec259ad2c2842666b2a0a49dea8463579c606426128925af1ed725"},
+ {file = "propcache-0.3.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:261fa020c1c14deafd54c76b014956e2f86991af198c51139faf41c4d5e83892"},
+ {file = "propcache-0.3.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:46d7f8aa79c927e5f987ee3a80205c987717d3659f035c85cf0c3680526bdb44"},
+ {file = "propcache-0.3.2-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:6d8f3f0eebf73e3c0ff0e7853f68be638b4043c65a70517bb575eff54edd8dbe"},
+ {file = "propcache-0.3.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:03c89c1b14a5452cf15403e291c0ccd7751d5b9736ecb2c5bab977ad6c5bcd81"},
+ {file = "propcache-0.3.2-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:0cc17efde71e12bbaad086d679ce575268d70bc123a5a71ea7ad76f70ba30bba"},
+ {file = "propcache-0.3.2-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:acdf05d00696bc0447e278bb53cb04ca72354e562cf88ea6f9107df8e7fd9770"},
+ {file = "propcache-0.3.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4445542398bd0b5d32df908031cb1b30d43ac848e20470a878b770ec2dcc6330"},
+ {file = "propcache-0.3.2-cp313-cp313t-win32.whl", hash = "sha256:f86e5d7cd03afb3a1db8e9f9f6eff15794e79e791350ac48a8c924e6f439f394"},
+ {file = "propcache-0.3.2-cp313-cp313t-win_amd64.whl", hash = "sha256:9704bedf6e7cbe3c65eca4379a9b53ee6a83749f047808cbb5044d40d7d72198"},
+ {file = "propcache-0.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a7fad897f14d92086d6b03fdd2eb844777b0c4d7ec5e3bac0fbae2ab0602bbe5"},
+ {file = "propcache-0.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1f43837d4ca000243fd7fd6301947d7cb93360d03cd08369969450cc6b2ce3b4"},
+ {file = "propcache-0.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:261df2e9474a5949c46e962065d88eb9b96ce0f2bd30e9d3136bcde84befd8f2"},
+ {file = "propcache-0.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e514326b79e51f0a177daab1052bc164d9d9e54133797a3a58d24c9c87a3fe6d"},
+ {file = "propcache-0.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d4a996adb6904f85894570301939afeee65f072b4fd265ed7e569e8d9058e4ec"},
+ {file = "propcache-0.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:76cace5d6b2a54e55b137669b30f31aa15977eeed390c7cbfb1dafa8dfe9a701"},
+ {file = "propcache-0.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31248e44b81d59d6addbb182c4720f90b44e1efdc19f58112a3c3a1615fb47ef"},
+ {file = "propcache-0.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abb7fa19dbf88d3857363e0493b999b8011eea856b846305d8c0512dfdf8fbb1"},
+ {file = "propcache-0.3.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:d81ac3ae39d38588ad0549e321e6f773a4e7cc68e7751524a22885d5bbadf886"},
+ {file = "propcache-0.3.2-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:cc2782eb0f7a16462285b6f8394bbbd0e1ee5f928034e941ffc444012224171b"},
+ {file = "propcache-0.3.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:db429c19a6c7e8a1c320e6a13c99799450f411b02251fb1b75e6217cf4a14fcb"},
+ {file = "propcache-0.3.2-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:21d8759141a9e00a681d35a1f160892a36fb6caa715ba0b832f7747da48fb6ea"},
+ {file = "propcache-0.3.2-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:2ca6d378f09adb13837614ad2754fa8afaee330254f404299611bce41a8438cb"},
+ {file = "propcache-0.3.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:34a624af06c048946709f4278b4176470073deda88d91342665d95f7c6270fbe"},
+ {file = "propcache-0.3.2-cp39-cp39-win32.whl", hash = "sha256:4ba3fef1c30f306b1c274ce0b8baaa2c3cdd91f645c48f06394068f37d3837a1"},
+ {file = "propcache-0.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:7a2368eed65fc69a7a7a40b27f22e85e7627b74216f0846b04ba5c116e191ec9"},
+ {file = "propcache-0.3.2-py3-none-any.whl", hash = "sha256:98f1ec44fb675f5052cccc8e609c46ed23a35a1cfd18545ad4e29002d858a43f"},
+ {file = "propcache-0.3.2.tar.gz", hash = "sha256:20d7d62e4e7ef05f221e0db2856b979540686342e7dd9973b815599c7057e168"},
+]
+
+[[package]]
+name = "protobuf"
+version = "6.32.0"
+description = ""
+optional = false
+python-versions = ">=3.9"
+groups = ["benchmarks"]
+files = [
+ {file = "protobuf-6.32.0-cp310-abi3-win32.whl", hash = "sha256:84f9e3c1ff6fb0308dbacb0950d8aa90694b0d0ee68e75719cb044b7078fe741"},
+ {file = "protobuf-6.32.0-cp310-abi3-win_amd64.whl", hash = "sha256:a8bdbb2f009cfc22a36d031f22a625a38b615b5e19e558a7b756b3279723e68e"},
+ {file = "protobuf-6.32.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:d52691e5bee6c860fff9a1c86ad26a13afbeb4b168cd4445c922b7e2cf85aaf0"},
+ {file = "protobuf-6.32.0-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:501fe6372fd1c8ea2a30b4d9be8f87955a64d6be9c88a973996cef5ef6f0abf1"},
+ {file = "protobuf-6.32.0-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:75a2aab2bd1aeb1f5dc7c5f33bcb11d82ea8c055c9becbb41c26a8c43fd7092c"},
+ {file = "protobuf-6.32.0-cp39-cp39-win32.whl", hash = "sha256:7db8ed09024f115ac877a1427557b838705359f047b2ff2f2b2364892d19dacb"},
+ {file = "protobuf-6.32.0-cp39-cp39-win_amd64.whl", hash = "sha256:15eba1b86f193a407607112ceb9ea0ba9569aed24f93333fe9a497cf2fda37d3"},
+ {file = "protobuf-6.32.0-py3-none-any.whl", hash = "sha256:ba377e5b67b908c8f3072a57b63e2c6a4cbd18aea4ed98d2584350dbf46f2783"},
+ {file = "protobuf-6.32.0.tar.gz", hash = "sha256:a81439049127067fc49ec1d36e25c6ee1d1a2b7be930675f919258d03c04e7d2"},
+]
+
+[[package]]
+name = "psutil"
+version = "7.0.0"
+description = "Cross-platform lib for process and system monitoring in Python. NOTE: the syntax of this script MUST be kept compatible with Python 2.7."
+optional = false
+python-versions = ">=3.6"
+groups = ["main"]
+files = [
+ {file = "psutil-7.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:101d71dc322e3cffd7cea0650b09b3d08b8e7c4109dd6809fe452dfd00e58b25"},
+ {file = "psutil-7.0.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:39db632f6bb862eeccf56660871433e111b6ea58f2caea825571951d4b6aa3da"},
+ {file = "psutil-7.0.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fcee592b4c6f146991ca55919ea3d1f8926497a713ed7faaf8225e174581e91"},
+ {file = "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"},
+ {file = "psutil-7.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5f098451abc2828f7dc6b58d44b532b22f2088f4999a937557b603ce72b1993"},
+ {file = "psutil-7.0.0-cp36-cp36m-win32.whl", hash = "sha256:84df4eb63e16849689f76b1ffcb36db7b8de703d1bc1fe41773db487621b6c17"},
+ {file = "psutil-7.0.0-cp36-cp36m-win_amd64.whl", hash = "sha256:1e744154a6580bc968a0195fd25e80432d3afec619daf145b9e5ba16cc1d688e"},
+ {file = "psutil-7.0.0-cp37-abi3-win32.whl", hash = "sha256:ba3fcef7523064a6c9da440fc4d6bd07da93ac726b5733c29027d7dc95b39d99"},
+ {file = "psutil-7.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:4cf3d4eb1aa9b348dec30105c55cd9b7d4629285735a102beb4441e38db90553"},
+ {file = "psutil-7.0.0.tar.gz", hash = "sha256:7be9c3eba38beccb6495ea33afd982a44074b78f28c434a1f51cc07fd315c456"},
+]
+
+[package.extras]
+dev = ["abi3audit", "black (==24.10.0)", "check-manifest", "coverage", "packaging", "pylint", "pyperf", "pypinfo", "pytest", "pytest-cov", "pytest-xdist", "requests", "rstcheck", "ruff", "setuptools", "sphinx", "sphinx_rtd_theme", "toml-sort", "twine", "virtualenv", "vulture", "wheel"]
+test = ["pytest", "pytest-xdist", "setuptools"]
+
+[[package]]
+name = "pyarrow"
+version = "21.0.0"
+description = "Python library for Apache Arrow"
+optional = false
+python-versions = ">=3.9"
+groups = ["main"]
+files = [
+ {file = "pyarrow-21.0.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:e563271e2c5ff4d4a4cbeb2c83d5cf0d4938b891518e676025f7268c6fe5fe26"},
+ {file = "pyarrow-21.0.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:fee33b0ca46f4c85443d6c450357101e47d53e6c3f008d658c27a2d020d44c79"},
+ {file = "pyarrow-21.0.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:7be45519b830f7c24b21d630a31d48bcebfd5d4d7f9d3bdb49da9cdf6d764edb"},
+ {file = "pyarrow-21.0.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:26bfd95f6bff443ceae63c65dc7e048670b7e98bc892210acba7e4995d3d4b51"},
+ {file = "pyarrow-21.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:bd04ec08f7f8bd113c55868bd3fc442a9db67c27af098c5f814a3091e71cc61a"},
+ {file = "pyarrow-21.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:9b0b14b49ac10654332a805aedfc0147fb3469cbf8ea951b3d040dab12372594"},
+ {file = "pyarrow-21.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:9d9f8bcb4c3be7738add259738abdeddc363de1b80e3310e04067aa1ca596634"},
+ {file = "pyarrow-21.0.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:c077f48aab61738c237802836fc3844f85409a46015635198761b0d6a688f87b"},
+ {file = "pyarrow-21.0.0-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:689f448066781856237eca8d1975b98cace19b8dd2ab6145bf49475478bcaa10"},
+ {file = "pyarrow-21.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:479ee41399fcddc46159a551705b89c05f11e8b8cb8e968f7fec64f62d91985e"},
+ {file = "pyarrow-21.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:40ebfcb54a4f11bcde86bc586cbd0272bac0d516cfa539c799c2453768477569"},
+ {file = "pyarrow-21.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8d58d8497814274d3d20214fbb24abcad2f7e351474357d552a8d53bce70c70e"},
+ {file = "pyarrow-21.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:585e7224f21124dd57836b1530ac8f2df2afc43c861d7bf3d58a4870c42ae36c"},
+ {file = "pyarrow-21.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:555ca6935b2cbca2c0e932bedd853e9bc523098c39636de9ad4693b5b1df86d6"},
+ {file = "pyarrow-21.0.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:3a302f0e0963db37e0a24a70c56cf91a4faa0bca51c23812279ca2e23481fccd"},
+ {file = "pyarrow-21.0.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:b6b27cf01e243871390474a211a7922bfbe3bda21e39bc9160daf0da3fe48876"},
+ {file = "pyarrow-21.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:e72a8ec6b868e258a2cd2672d91f2860ad532d590ce94cdf7d5e7ec674ccf03d"},
+ {file = "pyarrow-21.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:b7ae0bbdc8c6674259b25bef5d2a1d6af5d39d7200c819cf99e07f7dfef1c51e"},
+ {file = "pyarrow-21.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:58c30a1729f82d201627c173d91bd431db88ea74dcaa3885855bc6203e433b82"},
+ {file = "pyarrow-21.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:072116f65604b822a7f22945a7a6e581cfa28e3454fdcc6939d4ff6090126623"},
+ {file = "pyarrow-21.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:cf56ec8b0a5c8c9d7021d6fd754e688104f9ebebf1bf4449613c9531f5346a18"},
+ {file = "pyarrow-21.0.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:e99310a4ebd4479bcd1964dff9e14af33746300cb014aa4a3781738ac63baf4a"},
+ {file = "pyarrow-21.0.0-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:d2fe8e7f3ce329a71b7ddd7498b3cfac0eeb200c2789bd840234f0dc271a8efe"},
+ {file = "pyarrow-21.0.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:f522e5709379d72fb3da7785aa489ff0bb87448a9dc5a75f45763a795a089ebd"},
+ {file = "pyarrow-21.0.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:69cbbdf0631396e9925e048cfa5bce4e8c3d3b41562bbd70c685a8eb53a91e61"},
+ {file = "pyarrow-21.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:731c7022587006b755d0bdb27626a1a3bb004bb56b11fb30d98b6c1b4718579d"},
+ {file = "pyarrow-21.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:dc56bc708f2d8ac71bd1dcb927e458c93cec10b98eb4120206a4091db7b67b99"},
+ {file = "pyarrow-21.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:186aa00bca62139f75b7de8420f745f2af12941595bbbfa7ed3870ff63e25636"},
+ {file = "pyarrow-21.0.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:a7a102574faa3f421141a64c10216e078df467ab9576684d5cd696952546e2da"},
+ {file = "pyarrow-21.0.0-cp313-cp313t-macosx_12_0_x86_64.whl", hash = "sha256:1e005378c4a2c6db3ada3ad4c217b381f6c886f0a80d6a316fe586b90f77efd7"},
+ {file = "pyarrow-21.0.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:65f8e85f79031449ec8706b74504a316805217b35b6099155dd7e227eef0d4b6"},
+ {file = "pyarrow-21.0.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:3a81486adc665c7eb1a2bde0224cfca6ceaba344a82a971ef059678417880eb8"},
+ {file = "pyarrow-21.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:fc0d2f88b81dcf3ccf9a6ae17f89183762c8a94a5bdcfa09e05cfe413acf0503"},
+ {file = "pyarrow-21.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6299449adf89df38537837487a4f8d3bd91ec94354fdd2a7d30bc11c48ef6e79"},
+ {file = "pyarrow-21.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:222c39e2c70113543982c6b34f3077962b44fca38c0bd9e68bb6781534425c10"},
+ {file = "pyarrow-21.0.0-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:a7f6524e3747e35f80744537c78e7302cd41deee8baa668d56d55f77d9c464b3"},
+ {file = "pyarrow-21.0.0-cp39-cp39-macosx_12_0_x86_64.whl", hash = "sha256:203003786c9fd253ebcafa44b03c06983c9c8d06c3145e37f1b76a1f317aeae1"},
+ {file = "pyarrow-21.0.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:3b4d97e297741796fead24867a8dabf86c87e4584ccc03167e4a811f50fdf74d"},
+ {file = "pyarrow-21.0.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:898afce396b80fdda05e3086b4256f8677c671f7b1d27a6976fa011d3fd0a86e"},
+ {file = "pyarrow-21.0.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:067c66ca29aaedae08218569a114e413b26e742171f526e828e1064fcdec13f4"},
+ {file = "pyarrow-21.0.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:0c4e75d13eb76295a49e0ea056eb18dbd87d81450bfeb8afa19a7e5a75ae2ad7"},
+ {file = "pyarrow-21.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:cdc4c17afda4dab2a9c0b79148a43a7f4e1094916b3e18d8975bfd6d6d52241f"},
+ {file = "pyarrow-21.0.0.tar.gz", hash = "sha256:5051f2dccf0e283ff56335760cbc8622cf52264d67e359d5569541ac11b6d5bc"},
+]
+
+[package.extras]
+test = ["cffi", "hypothesis", "pandas", "pytest", "pytz"]
+
+[[package]]
+name = "pycodestyle"
+version = "2.9.1"
+description = "Python style guide checker"
+optional = false
+python-versions = ">=3.6"
+groups = ["dev"]
+files = [
+ {file = "pycodestyle-2.9.1-py2.py3-none-any.whl", hash = "sha256:d1735fc58b418fd7c5f658d28d943854f8a849b01a5d0a1e6f3f3fdd0166804b"},
+ {file = "pycodestyle-2.9.1.tar.gz", hash = "sha256:2c9607871d58c76354b697b42f5d57e1ada7d261c261efac224b664affdc5785"},
+]
+
+[[package]]
+name = "pydantic"
+version = "2.11.7"
+description = "Data validation using Python type hints"
+optional = false
+python-versions = ">=3.9"
+groups = ["benchmarks"]
+files = [
+ {file = "pydantic-2.11.7-py3-none-any.whl", hash = "sha256:dde5df002701f6de26248661f6835bbe296a47bf73990135c7d07ce741b9623b"},
+ {file = "pydantic-2.11.7.tar.gz", hash = "sha256:d989c3c6cb79469287b1569f7447a17848c998458d49ebe294e975b9baf0f0db"},
+]
+
+[package.dependencies]
+annotated-types = ">=0.6.0"
+pydantic-core = "2.33.2"
+typing-extensions = ">=4.12.2"
+typing-inspection = ">=0.4.0"
+
+[package.extras]
+email = ["email-validator (>=2.0.0)"]
+timezone = ["tzdata ; python_version >= \"3.9\" and platform_system == \"Windows\""]
+
+[[package]]
+name = "pydantic-core"
+version = "2.33.2"
+description = "Core functionality for Pydantic validation and serialization"
+optional = false
+python-versions = ">=3.9"
+groups = ["benchmarks"]
+files = [
+ {file = "pydantic_core-2.33.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2b3d326aaef0c0399d9afffeb6367d5e26ddc24d351dbc9c636840ac355dc5d8"},
+ {file = "pydantic_core-2.33.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e5b2671f05ba48b94cb90ce55d8bdcaaedb8ba00cc5359f6810fc918713983d"},
+ {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0069c9acc3f3981b9ff4cdfaf088e98d83440a4c7ea1bc07460af3d4dc22e72d"},
+ {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d53b22f2032c42eaaf025f7c40c2e3b94568ae077a606f006d206a463bc69572"},
+ {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0405262705a123b7ce9f0b92f123334d67b70fd1f20a9372b907ce1080c7ba02"},
+ {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4b25d91e288e2c4e0662b8038a28c6a07eaac3e196cfc4ff69de4ea3db992a1b"},
+ {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bdfe4b3789761f3bcb4b1ddf33355a71079858958e3a552f16d5af19768fef2"},
+ {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:efec8db3266b76ef9607c2c4c419bdb06bf335ae433b80816089ea7585816f6a"},
+ {file = "pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:031c57d67ca86902726e0fae2214ce6770bbe2f710dc33063187a68744a5ecac"},
+ {file = "pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:f8de619080e944347f5f20de29a975c2d815d9ddd8be9b9b7268e2e3ef68605a"},
+ {file = "pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:73662edf539e72a9440129f231ed3757faab89630d291b784ca99237fb94db2b"},
+ {file = "pydantic_core-2.33.2-cp310-cp310-win32.whl", hash = "sha256:0a39979dcbb70998b0e505fb1556a1d550a0781463ce84ebf915ba293ccb7e22"},
+ {file = "pydantic_core-2.33.2-cp310-cp310-win_amd64.whl", hash = "sha256:b0379a2b24882fef529ec3b4987cb5d003b9cda32256024e6fe1586ac45fc640"},
+ {file = "pydantic_core-2.33.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4c5b0a576fb381edd6d27f0a85915c6daf2f8138dc5c267a57c08a62900758c7"},
+ {file = "pydantic_core-2.33.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e799c050df38a639db758c617ec771fd8fb7a5f8eaaa4b27b101f266b216a246"},
+ {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc46a01bf8d62f227d5ecee74178ffc448ff4e5197c756331f71efcc66dc980f"},
+ {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a144d4f717285c6d9234a66778059f33a89096dfb9b39117663fd8413d582dcc"},
+ {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73cf6373c21bc80b2e0dc88444f41ae60b2f070ed02095754eb5a01df12256de"},
+ {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3dc625f4aa79713512d1976fe9f0bc99f706a9dee21dfd1810b4bbbf228d0e8a"},
+ {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b21b5549499972441da4758d662aeea93f1923f953e9cbaff14b8b9565aef"},
+ {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bdc25f3681f7b78572699569514036afe3c243bc3059d3942624e936ec93450e"},
+ {file = "pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fe5b32187cbc0c862ee201ad66c30cf218e5ed468ec8dc1cf49dec66e160cc4d"},
+ {file = "pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:bc7aee6f634a6f4a95676fcb5d6559a2c2a390330098dba5e5a5f28a2e4ada30"},
+ {file = "pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:235f45e5dbcccf6bd99f9f472858849f73d11120d76ea8707115415f8e5ebebf"},
+ {file = "pydantic_core-2.33.2-cp311-cp311-win32.whl", hash = "sha256:6368900c2d3ef09b69cb0b913f9f8263b03786e5b2a387706c5afb66800efd51"},
+ {file = "pydantic_core-2.33.2-cp311-cp311-win_amd64.whl", hash = "sha256:1e063337ef9e9820c77acc768546325ebe04ee38b08703244c1309cccc4f1bab"},
+ {file = "pydantic_core-2.33.2-cp311-cp311-win_arm64.whl", hash = "sha256:6b99022f1d19bc32a4c2a0d544fc9a76e3be90f0b3f4af413f87d38749300e65"},
+ {file = "pydantic_core-2.33.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a7ec89dc587667f22b6a0b6579c249fca9026ce7c333fc142ba42411fa243cdc"},
+ {file = "pydantic_core-2.33.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3c6db6e52c6d70aa0d00d45cdb9b40f0433b96380071ea80b09277dba021ddf7"},
+ {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e61206137cbc65e6d5256e1166f88331d3b6238e082d9f74613b9b765fb9025"},
+ {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb8c529b2819c37140eb51b914153063d27ed88e3bdc31b71198a198e921e011"},
+ {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c52b02ad8b4e2cf14ca7b3d918f3eb0ee91e63b3167c32591e57c4317e134f8f"},
+ {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96081f1605125ba0855dfda83f6f3df5ec90c61195421ba72223de35ccfb2f88"},
+ {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f57a69461af2a5fa6e6bbd7a5f60d3b7e6cebb687f55106933188e79ad155c1"},
+ {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:572c7e6c8bb4774d2ac88929e3d1f12bc45714ae5ee6d9a788a9fb35e60bb04b"},
+ {file = "pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:db4b41f9bd95fbe5acd76d89920336ba96f03e149097365afe1cb092fceb89a1"},
+ {file = "pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:fa854f5cf7e33842a892e5c73f45327760bc7bc516339fda888c75ae60edaeb6"},
+ {file = "pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5f483cfb75ff703095c59e365360cb73e00185e01aaea067cd19acffd2ab20ea"},
+ {file = "pydantic_core-2.33.2-cp312-cp312-win32.whl", hash = "sha256:9cb1da0f5a471435a7bc7e439b8a728e8b61e59784b2af70d7c169f8dd8ae290"},
+ {file = "pydantic_core-2.33.2-cp312-cp312-win_amd64.whl", hash = "sha256:f941635f2a3d96b2973e867144fde513665c87f13fe0e193c158ac51bfaaa7b2"},
+ {file = "pydantic_core-2.33.2-cp312-cp312-win_arm64.whl", hash = "sha256:cca3868ddfaccfbc4bfb1d608e2ccaaebe0ae628e1416aeb9c4d88c001bb45ab"},
+ {file = "pydantic_core-2.33.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f"},
+ {file = "pydantic_core-2.33.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6"},
+ {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef"},
+ {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a"},
+ {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916"},
+ {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a"},
+ {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d"},
+ {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56"},
+ {file = "pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5"},
+ {file = "pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e"},
+ {file = "pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162"},
+ {file = "pydantic_core-2.33.2-cp313-cp313-win32.whl", hash = "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849"},
+ {file = "pydantic_core-2.33.2-cp313-cp313-win_amd64.whl", hash = "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9"},
+ {file = "pydantic_core-2.33.2-cp313-cp313-win_arm64.whl", hash = "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9"},
+ {file = "pydantic_core-2.33.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac"},
+ {file = "pydantic_core-2.33.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5"},
+ {file = "pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9"},
+ {file = "pydantic_core-2.33.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a2b911a5b90e0374d03813674bf0a5fbbb7741570dcd4b4e85a2e48d17def29d"},
+ {file = "pydantic_core-2.33.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6fa6dfc3e4d1f734a34710f391ae822e0a8eb8559a85c6979e14e65ee6ba2954"},
+ {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c54c939ee22dc8e2d545da79fc5381f1c020d6d3141d3bd747eab59164dc89fb"},
+ {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:53a57d2ed685940a504248187d5685e49eb5eef0f696853647bf37c418c538f7"},
+ {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09fb9dd6571aacd023fe6aaca316bd01cf60ab27240d7eb39ebd66a3a15293b4"},
+ {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0e6116757f7959a712db11f3e9c0a99ade00a5bbedae83cb801985aa154f071b"},
+ {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d55ab81c57b8ff8548c3e4947f119551253f4e3787a7bbc0b6b3ca47498a9d3"},
+ {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c20c462aa4434b33a2661701b861604913f912254e441ab8d78d30485736115a"},
+ {file = "pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:44857c3227d3fb5e753d5fe4a3420d6376fa594b07b621e220cd93703fe21782"},
+ {file = "pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:eb9b459ca4df0e5c87deb59d37377461a538852765293f9e6ee834f0435a93b9"},
+ {file = "pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9fcd347d2cc5c23b06de6d3b7b8275be558a0c90549495c699e379a80bf8379e"},
+ {file = "pydantic_core-2.33.2-cp39-cp39-win32.whl", hash = "sha256:83aa99b1285bc8f038941ddf598501a86f1536789740991d7d8756e34f1e74d9"},
+ {file = "pydantic_core-2.33.2-cp39-cp39-win_amd64.whl", hash = "sha256:f481959862f57f29601ccced557cc2e817bce7533ab8e01a797a48b49c9692b3"},
+ {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5c4aa4e82353f65e548c476b37e64189783aa5384903bfea4f41580f255fddfa"},
+ {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d946c8bf0d5c24bf4fe333af284c59a19358aa3ec18cb3dc4370080da1e8ad29"},
+ {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87b31b6846e361ef83fedb187bb5b4372d0da3f7e28d85415efa92d6125d6e6d"},
+ {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa9d91b338f2df0508606f7009fde642391425189bba6d8c653afd80fd6bb64e"},
+ {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2058a32994f1fde4ca0480ab9d1e75a0e8c87c22b53a3ae66554f9af78f2fe8c"},
+ {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:0e03262ab796d986f978f79c943fc5f620381be7287148b8010b4097f79a39ec"},
+ {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:1a8695a8d00c73e50bff9dfda4d540b7dee29ff9b8053e38380426a85ef10052"},
+ {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:fa754d1850735a0b0e03bcffd9d4b4343eb417e47196e4485d9cca326073a42c"},
+ {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a11c8d26a50bfab49002947d3d237abe4d9e4b5bdc8846a63537b6488e197808"},
+ {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:dd14041875d09cc0f9308e37a6f8b65f5585cf2598a53aa0123df8b129d481f8"},
+ {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d87c561733f66531dced0da6e864f44ebf89a8fba55f31407b00c2f7f9449593"},
+ {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f82865531efd18d6e07a04a17331af02cb7a651583c418df8266f17a63c6612"},
+ {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bfb5112df54209d820d7bf9317c7a6c9025ea52e49f46b6a2060104bba37de7"},
+ {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:64632ff9d614e5eecfb495796ad51b0ed98c453e447a76bcbeeb69615079fc7e"},
+ {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:f889f7a40498cc077332c7ab6b4608d296d852182211787d4f3ee377aaae66e8"},
+ {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:de4b83bb311557e439b9e186f733f6c645b9417c84e2eb8203f3f820a4b988bf"},
+ {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:82f68293f055f51b51ea42fafc74b6aad03e70e191799430b90c13d643059ebb"},
+ {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:329467cecfb529c925cf2bbd4d60d2c509bc2fb52a20c1045bf09bb70971a9c1"},
+ {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:87acbfcf8e90ca885206e98359d7dca4bcbb35abdc0ff66672a293e1d7a19101"},
+ {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:7f92c15cd1e97d4b12acd1cc9004fa092578acfa57b67ad5e43a197175d01a64"},
+ {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3f26877a748dc4251cfcfda9dfb5f13fcb034f5308388066bcfe9031b63ae7d"},
+ {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac89aea9af8cd672fa7b510e7b8c33b0bba9a43186680550ccf23020f32d535"},
+ {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:970919794d126ba8645f3837ab6046fb4e72bbc057b3709144066204c19a455d"},
+ {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:3eb3fe62804e8f859c49ed20a8451342de53ed764150cb14ca71357c765dc2a6"},
+ {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:3abcd9392a36025e3bd55f9bd38d908bd17962cc49bc6da8e7e96285336e2bca"},
+ {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:3a1c81334778f9e3af2f8aeb7a960736e5cab1dfebfb26aabca09afd2906c039"},
+ {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2807668ba86cb38c6817ad9bc66215ab8584d1d304030ce4f0887336f28a5e27"},
+ {file = "pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc"},
+]
+
+[package.dependencies]
+typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0"
+
+[[package]]
+name = "pyflakes"
+version = "2.5.0"
+description = "passive checker of Python programs"
+optional = false
+python-versions = ">=3.6"
+groups = ["dev"]
+files = [
+ {file = "pyflakes-2.5.0-py2.py3-none-any.whl", hash = "sha256:4579f67d887f804e67edb544428f264b7b24f435b263c4614f384135cea553d2"},
+ {file = "pyflakes-2.5.0.tar.gz", hash = "sha256:491feb020dca48ccc562a8c0cbe8df07ee13078df59813b83959cbdada312ea3"},
+]
+
+[[package]]
+name = "pygments"
+version = "2.19.2"
+description = "Pygments is a syntax highlighting package written in Python."
+optional = false
+python-versions = ">=3.8"
+groups = ["dev", "docs"]
+files = [
+ {file = "pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b"},
+ {file = "pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887"},
+]
+
+[package.extras]
+windows-terminal = ["colorama (>=0.4.6)"]
+
+[[package]]
+name = "pylint"
+version = "2.17.4"
+description = "python code static checker"
+optional = false
+python-versions = ">=3.7.2"
+groups = ["dev"]
+files = [
+ {file = "pylint-2.17.4-py3-none-any.whl", hash = "sha256:7a1145fb08c251bdb5cca11739722ce64a63db479283d10ce718b2460e54123c"},
+ {file = "pylint-2.17.4.tar.gz", hash = "sha256:5dcf1d9e19f41f38e4e85d10f511e5b9c35e1aa74251bf95cdd8cb23584e2db1"},
+]
+
+[package.dependencies]
+astroid = ">=2.15.4,<=2.17.0-dev0"
+colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""}
+dill = [
+ {version = ">=0.2", markers = "python_version < \"3.11\""},
+ {version = ">=0.3.6", markers = "python_version >= \"3.11\""},
+]
+isort = ">=4.2.5,<6"
+mccabe = ">=0.6,<0.8"
+platformdirs = ">=2.2.0"
+tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
+tomlkit = ">=0.10.1"
+typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""}
+
+[package.extras]
+spelling = ["pyenchant (>=3.2,<4.0)"]
+testutils = ["gitpython (>3)"]
+
+[[package]]
+name = "pynvml"
+version = "12.0.0"
+description = "Python utilities for the NVIDIA Management Library"
+optional = false
+python-versions = ">=3.9"
+groups = ["main"]
+files = [
+ {file = "pynvml-12.0.0-py3-none-any.whl", hash = "sha256:fdff84b62a27dbe98e08e1a647eb77342bef1aebe0878bcd15e99a83fcbecb9e"},
+ {file = "pynvml-12.0.0.tar.gz", hash = "sha256:299ce2451a6a17e6822d6faee750103e25b415f06f59abb8db65d30f794166f5"},
+]
+
+[package.dependencies]
+nvidia-ml-py = ">=12.0.0,<13.0.0a0"
+
+[package.extras]
+test = ["pytest (>=3.6)", "pytest-cov", "pytest-runner"]
+
+[[package]]
+name = "pyparsing"
+version = "3.2.3"
+description = "pyparsing module - Classes and methods to define and execute parsing grammars"
+optional = false
+python-versions = ">=3.9"
+groups = ["main"]
+files = [
+ {file = "pyparsing-3.2.3-py3-none-any.whl", hash = "sha256:a749938e02d6fd0b59b356ca504a24982314bb090c383e3cf201c95ef7e2bfcf"},
+ {file = "pyparsing-3.2.3.tar.gz", hash = "sha256:b9c13f1ab8b3b542f72e28f634bad4de758ab3ce4546e4301970ad6fa77c38be"},
+]
+
+[package.extras]
+diagrams = ["jinja2", "railroad-diagrams"]
+
+[[package]]
+name = "pytest"
+version = "8.4.1"
+description = "pytest: simple powerful testing with Python"
+optional = false
+python-versions = ">=3.9"
+groups = ["dev"]
+files = [
+ {file = "pytest-8.4.1-py3-none-any.whl", hash = "sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7"},
+ {file = "pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c"},
+]
+
+[package.dependencies]
+colorama = {version = ">=0.4", markers = "sys_platform == \"win32\""}
+exceptiongroup = {version = ">=1", markers = "python_version < \"3.11\""}
+iniconfig = ">=1"
+packaging = ">=20"
+pluggy = ">=1.5,<2"
+pygments = ">=2.7.2"
+tomli = {version = ">=1", markers = "python_version < \"3.11\""}
+
+[package.extras]
+dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests", "setuptools", "xmlschema"]
+
+[[package]]
+name = "pytest-cov"
+version = "6.2.1"
+description = "Pytest plugin for measuring coverage."
+optional = false
+python-versions = ">=3.9"
+groups = ["dev"]
+files = [
+ {file = "pytest_cov-6.2.1-py3-none-any.whl", hash = "sha256:f5bc4c23f42f1cdd23c70b1dab1bbaef4fc505ba950d53e0081d0730dd7e86d5"},
+ {file = "pytest_cov-6.2.1.tar.gz", hash = "sha256:25cc6cc0a5358204b8108ecedc51a9b57b34cc6b8c967cc2c01a4e00d8a67da2"},
+]
+
+[package.dependencies]
+coverage = {version = ">=7.5", extras = ["toml"]}
+pluggy = ">=1.2"
+pytest = ">=6.2.5"
+
+[package.extras]
+testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"]
+
+[[package]]
+name = "pytest-mock"
+version = "3.14.1"
+description = "Thin-wrapper around the mock package for easier use with pytest"
+optional = false
+python-versions = ">=3.8"
+groups = ["dev"]
+files = [
+ {file = "pytest_mock-3.14.1-py3-none-any.whl", hash = "sha256:178aefcd11307d874b4cd3100344e7e2d888d9791a6a1d9bfe90fbc1b74fd1d0"},
+ {file = "pytest_mock-3.14.1.tar.gz", hash = "sha256:159e9edac4c451ce77a5cdb9fc5d1100708d2dd4ba3c3df572f14097351af80e"},
+]
+
+[package.dependencies]
+pytest = ">=6.2.5"
+
+[package.extras]
+dev = ["pre-commit", "pytest-asyncio", "tox"]
+
+[[package]]
+name = "pytest-xdist"
+version = "3.8.0"
+description = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs"
+optional = false
+python-versions = ">=3.9"
+groups = ["dev"]
+files = [
+ {file = "pytest_xdist-3.8.0-py3-none-any.whl", hash = "sha256:202ca578cfeb7370784a8c33d6d05bc6e13b4f25b5053c30a152269fd10f0b88"},
+ {file = "pytest_xdist-3.8.0.tar.gz", hash = "sha256:7e578125ec9bc6050861aa93f2d59f1d8d085595d6551c2c90b6f4fad8d3a9f1"},
+]
+
+[package.dependencies]
+execnet = ">=2.1"
+pytest = ">=7.0.0"
+
+[package.extras]
+psutil = ["psutil (>=3.0)"]
+setproctitle = ["setproctitle"]
+testing = ["filelock"]
+
+[[package]]
+name = "python-dateutil"
+version = "2.9.0.post0"
+description = "Extensions to the standard Python datetime module"
+optional = false
+python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
+groups = ["main"]
+files = [
+ {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"},
+ {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"},
+]
+
+[package.dependencies]
+six = ">=1.5"
+
+[[package]]
+name = "pytz"
+version = "2025.2"
+description = "World timezone definitions, modern and historical"
+optional = false
+python-versions = "*"
+groups = ["main"]
+files = [
+ {file = "pytz-2025.2-py2.py3-none-any.whl", hash = "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00"},
+ {file = "pytz-2025.2.tar.gz", hash = "sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3"},
+]
+
+[[package]]
+name = "pyupgrade"
+version = "3.20.0"
+description = "A tool to automatically upgrade syntax for newer versions."
+optional = false
+python-versions = ">=3.9"
+groups = ["dev"]
+files = [
+ {file = "pyupgrade-3.20.0-py2.py3-none-any.whl", hash = "sha256:cd5bf842b863f50adad324a01c30aef60b9f698a9814848094818659c92cd1f4"},
+ {file = "pyupgrade-3.20.0.tar.gz", hash = "sha256:dd6a16c13fc1a7db45796008689a9a35420bd364d681430f640c5e54a3d351ea"},
+]
+
+[package.dependencies]
+tokenize-rt = ">=6.1.0"
+
+[[package]]
+name = "pyyaml"
+version = "6.0.2"
+description = "YAML parser and emitter for Python"
+optional = false
+python-versions = ">=3.8"
+groups = ["main", "benchmarks", "dev", "docs"]
+files = [
+ {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"},
+ {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"},
+ {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"},
+ {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"},
+ {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"},
+ {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"},
+ {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"},
+ {file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"},
+ {file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"},
+ {file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"},
+ {file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"},
+ {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"},
+ {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"},
+ {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"},
+ {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"},
+ {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"},
+ {file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"},
+ {file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"},
+ {file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"},
+ {file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"},
+ {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"},
+ {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"},
+ {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"},
+ {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"},
+ {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"},
+ {file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"},
+ {file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"},
+ {file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"},
+ {file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"},
+ {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"},
+ {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"},
+ {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"},
+ {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"},
+ {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"},
+ {file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"},
+ {file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"},
+ {file = "PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a"},
+ {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5"},
+ {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d"},
+ {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083"},
+ {file = "PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706"},
+ {file = "PyYAML-6.0.2-cp38-cp38-win32.whl", hash = "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a"},
+ {file = "PyYAML-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff"},
+ {file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"},
+ {file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"},
+ {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"},
+ {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"},
+ {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"},
+ {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"},
+ {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"},
+ {file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"},
+ {file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"},
+ {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"},
+]
+
+[[package]]
+name = "regex"
+version = "2025.9.1"
+description = "Alternative regular expression module, to replace re."
+optional = false
+python-versions = ">=3.9"
+groups = ["main"]
+files = [
+ {file = "regex-2025.9.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c5aa2a6a73bf218515484b36a0d20c6ad9dc63f6339ff6224147b0e2c095ee55"},
+ {file = "regex-2025.9.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8c2ff5c01d5e47ad5fc9d31bcd61e78c2fa0068ed00cab86b7320214446da766"},
+ {file = "regex-2025.9.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d49dc84e796b666181de8a9973284cad6616335f01b52bf099643253094920fc"},
+ {file = "regex-2025.9.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d9914fe1040874f83c15fcea86d94ea54091b0666eab330aaab69e30d106aabe"},
+ {file = "regex-2025.9.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e71bceb3947362ec5eabd2ca0870bb78eae4edfc60c6c21495133c01b6cd2df4"},
+ {file = "regex-2025.9.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:67a74456f410fe5e869239ee7a5423510fe5121549af133809d9591a8075893f"},
+ {file = "regex-2025.9.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5c3b96ed0223b32dbdc53a83149b6de7ca3acd5acd9c8e64b42a166228abe29c"},
+ {file = "regex-2025.9.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:113d5aa950f428faf46fd77d452df62ebb4cc6531cb619f6cc30a369d326bfbd"},
+ {file = "regex-2025.9.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:fcdeb38de4f7f3d69d798f4f371189061446792a84e7c92b50054c87aae9c07c"},
+ {file = "regex-2025.9.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4bcdff370509164b67a6c8ec23c9fb40797b72a014766fdc159bb809bd74f7d8"},
+ {file = "regex-2025.9.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:7383efdf6e8e8c61d85e00cfb2e2e18da1a621b8bfb4b0f1c2747db57b942b8f"},
+ {file = "regex-2025.9.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1ec2bd3bdf0f73f7e9f48dca550ba7d973692d5e5e9a90ac42cc5f16c4432d8b"},
+ {file = "regex-2025.9.1-cp310-cp310-win32.whl", hash = "sha256:9627e887116c4e9c0986d5c3b4f52bcfe3df09850b704f62ec3cbf177a0ae374"},
+ {file = "regex-2025.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:94533e32dc0065eca43912ee6649c90ea0681d59f56d43c45b5bcda9a740b3dd"},
+ {file = "regex-2025.9.1-cp310-cp310-win_arm64.whl", hash = "sha256:a874a61bb580d48642ffd338570ee24ab13fa023779190513fcacad104a6e251"},
+ {file = "regex-2025.9.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e5bcf112b09bfd3646e4db6bf2e598534a17d502b0c01ea6550ba4eca780c5e6"},
+ {file = "regex-2025.9.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:67a0295a3c31d675a9ee0238d20238ff10a9a2fdb7a1323c798fc7029578b15c"},
+ {file = "regex-2025.9.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ea8267fbadc7d4bd7c1301a50e85c2ff0de293ff9452a1a9f8d82c6cafe38179"},
+ {file = "regex-2025.9.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6aeff21de7214d15e928fb5ce757f9495214367ba62875100d4c18d293750cc1"},
+ {file = "regex-2025.9.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d89f1bbbbbc0885e1c230f7770d5e98f4f00b0ee85688c871d10df8b184a6323"},
+ {file = "regex-2025.9.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ca3affe8ddea498ba9d294ab05f5f2d3b5ad5d515bc0d4a9016dd592a03afe52"},
+ {file = "regex-2025.9.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:91892a7a9f0a980e4c2c85dd19bc14de2b219a3a8867c4b5664b9f972dcc0c78"},
+ {file = "regex-2025.9.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e1cb40406f4ae862710615f9f636c1e030fd6e6abe0e0f65f6a695a2721440c6"},
+ {file = "regex-2025.9.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:94f6cff6f7e2149c7e6499a6ecd4695379eeda8ccbccb9726e8149f2fe382e92"},
+ {file = "regex-2025.9.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:6c0226fb322b82709e78c49cc33484206647f8a39954d7e9de1567f5399becd0"},
+ {file = "regex-2025.9.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a12f59c7c380b4fcf7516e9cbb126f95b7a9518902bcf4a852423ff1dcd03e6a"},
+ {file = "regex-2025.9.1-cp311-cp311-win32.whl", hash = "sha256:49865e78d147a7a4f143064488da5d549be6bfc3f2579e5044cac61f5c92edd4"},
+ {file = "regex-2025.9.1-cp311-cp311-win_amd64.whl", hash = "sha256:d34b901f6f2f02ef60f4ad3855d3a02378c65b094efc4b80388a3aeb700a5de7"},
+ {file = "regex-2025.9.1-cp311-cp311-win_arm64.whl", hash = "sha256:47d7c2dab7e0b95b95fd580087b6ae196039d62306a592fa4e162e49004b6299"},
+ {file = "regex-2025.9.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:84a25164bd8dcfa9f11c53f561ae9766e506e580b70279d05a7946510bdd6f6a"},
+ {file = "regex-2025.9.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:645e88a73861c64c1af558dd12294fb4e67b5c1eae0096a60d7d8a2143a611c7"},
+ {file = "regex-2025.9.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:10a450cba5cd5409526ee1d4449f42aad38dd83ac6948cbd6d7f71ca7018f7db"},
+ {file = "regex-2025.9.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e9dc5991592933a4192c166eeb67b29d9234f9c86344481173d1bc52f73a7104"},
+ {file = "regex-2025.9.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a32291add816961aab472f4fad344c92871a2ee33c6c219b6598e98c1f0108f2"},
+ {file = "regex-2025.9.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:588c161a68a383478e27442a678e3b197b13c5ba51dbba40c1ccb8c4c7bee9e9"},
+ {file = "regex-2025.9.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:47829ffaf652f30d579534da9085fe30c171fa2a6744a93d52ef7195dc38218b"},
+ {file = "regex-2025.9.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1e978e5a35b293ea43f140c92a3269b6ab13fe0a2bf8a881f7ac740f5a6ade85"},
+ {file = "regex-2025.9.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4cf09903e72411f4bf3ac1eddd624ecfd423f14b2e4bf1c8b547b72f248b7bf7"},
+ {file = "regex-2025.9.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:d016b0f77be63e49613c9e26aaf4a242f196cd3d7a4f15898f5f0ab55c9b24d2"},
+ {file = "regex-2025.9.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:656563e620de6908cd1c9d4f7b9e0777e3341ca7db9d4383bcaa44709c90281e"},
+ {file = "regex-2025.9.1-cp312-cp312-win32.whl", hash = "sha256:df33f4ef07b68f7ab637b1dbd70accbf42ef0021c201660656601e8a9835de45"},
+ {file = "regex-2025.9.1-cp312-cp312-win_amd64.whl", hash = "sha256:5aba22dfbc60cda7c0853516104724dc904caa2db55f2c3e6e984eb858d3edf3"},
+ {file = "regex-2025.9.1-cp312-cp312-win_arm64.whl", hash = "sha256:ec1efb4c25e1849c2685fa95da44bfde1b28c62d356f9c8d861d4dad89ed56e9"},
+ {file = "regex-2025.9.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:bc6834727d1b98d710a63e6c823edf6ffbf5792eba35d3fa119531349d4142ef"},
+ {file = "regex-2025.9.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c3dc05b6d579875719bccc5f3037b4dc80433d64e94681a0061845bd8863c025"},
+ {file = "regex-2025.9.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:22213527df4c985ec4a729b055a8306272d41d2f45908d7bacb79be0fa7a75ad"},
+ {file = "regex-2025.9.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8e3f6e3c5a5a1adc3f7ea1b5aec89abfc2f4fbfba55dafb4343cd1d084f715b2"},
+ {file = "regex-2025.9.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:bcb89c02a0d6c2bec9b0bb2d8c78782699afe8434493bfa6b4021cc51503f249"},
+ {file = "regex-2025.9.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b0e2f95413eb0c651cd1516a670036315b91b71767af83bc8525350d4375ccba"},
+ {file = "regex-2025.9.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:09a41dc039e1c97d3c2ed3e26523f748e58c4de3ea7a31f95e1cf9ff973fff5a"},
+ {file = "regex-2025.9.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4f0b4258b161094f66857a26ee938d3fe7b8a5063861e44571215c44fbf0e5df"},
+ {file = "regex-2025.9.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:bf70e18ac390e6977ea7e56f921768002cb0fa359c4199606c7219854ae332e0"},
+ {file = "regex-2025.9.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b84036511e1d2bb0a4ff1aec26951caa2dea8772b223c9e8a19ed8885b32dbac"},
+ {file = "regex-2025.9.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c2e05dcdfe224047f2a59e70408274c325d019aad96227ab959403ba7d58d2d7"},
+ {file = "regex-2025.9.1-cp313-cp313-win32.whl", hash = "sha256:3b9a62107a7441b81ca98261808fed30ae36ba06c8b7ee435308806bd53c1ed8"},
+ {file = "regex-2025.9.1-cp313-cp313-win_amd64.whl", hash = "sha256:b38afecc10c177eb34cfae68d669d5161880849ba70c05cbfbe409f08cc939d7"},
+ {file = "regex-2025.9.1-cp313-cp313-win_arm64.whl", hash = "sha256:ec329890ad5e7ed9fc292858554d28d58d56bf62cf964faf0aa57964b21155a0"},
+ {file = "regex-2025.9.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:72fb7a016467d364546f22b5ae86c45680a4e0de6b2a6f67441d22172ff641f1"},
+ {file = "regex-2025.9.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:c9527fa74eba53f98ad86be2ba003b3ebe97e94b6eb2b916b31b5f055622ef03"},
+ {file = "regex-2025.9.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c905d925d194c83a63f92422af7544ec188301451b292c8b487f0543726107ca"},
+ {file = "regex-2025.9.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:74df7c74a63adcad314426b1f4ea6054a5ab25d05b0244f0c07ff9ce640fa597"},
+ {file = "regex-2025.9.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4f6e935e98ea48c7a2e8be44494de337b57a204470e7f9c9c42f912c414cd6f5"},
+ {file = "regex-2025.9.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4a62d033cd9ebefc7c5e466731a508dfabee827d80b13f455de68a50d3c2543d"},
+ {file = "regex-2025.9.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ef971ebf2b93bdc88d8337238be4dfb851cc97ed6808eb04870ef67589415171"},
+ {file = "regex-2025.9.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d936a1db208bdca0eca1f2bb2c1ba1d8370b226785c1e6db76e32a228ffd0ad5"},
+ {file = "regex-2025.9.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:7e786d9e4469698fc63815b8de08a89165a0aa851720eb99f5e0ea9d51dd2b6a"},
+ {file = "regex-2025.9.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:6b81d7dbc5466ad2c57ce3a0ddb717858fe1a29535c8866f8514d785fdb9fc5b"},
+ {file = "regex-2025.9.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:cd4890e184a6feb0ef195338a6ce68906a8903a0f2eb7e0ab727dbc0a3156273"},
+ {file = "regex-2025.9.1-cp314-cp314-win32.whl", hash = "sha256:34679a86230e46164c9e0396b56cab13c0505972343880b9e705083cc5b8ec86"},
+ {file = "regex-2025.9.1-cp314-cp314-win_amd64.whl", hash = "sha256:a1196e530a6bfa5f4bde029ac5b0295a6ecfaaffbfffede4bbaf4061d9455b70"},
+ {file = "regex-2025.9.1-cp314-cp314-win_arm64.whl", hash = "sha256:f46d525934871ea772930e997d577d48c6983e50f206ff7b66d4ac5f8941e993"},
+ {file = "regex-2025.9.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a13d20007dce3c4b00af5d84f6c191ed1c0f70928c6d9b6cd7b8d2f125df7f46"},
+ {file = "regex-2025.9.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d6b046b0a01cb713fd53ef36cb59db4b0062b343db28e83b52ac6aa01ee5b368"},
+ {file = "regex-2025.9.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0fa9a7477288717f42dbd02ff5d13057549e9a8cdb81f224c313154cc10bab52"},
+ {file = "regex-2025.9.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b2b3ad150c6bc01a8cd5030040675060e2adbe6cbc50aadc4da42c6d32ec266e"},
+ {file = "regex-2025.9.1-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:aa88d5a82dfe80deaf04e8c39c8b0ad166d5d527097eb9431cb932c44bf88715"},
+ {file = "regex-2025.9.1-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6f1dae2cf6c2dbc6fd2526653692c144721b3cf3f769d2a3c3aa44d0f38b9a58"},
+ {file = "regex-2025.9.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ff62a3022914fc19adaa76b65e03cf62bc67ea16326cbbeb170d280710a7d719"},
+ {file = "regex-2025.9.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a34ef82216189d823bc82f614d1031cb0b919abef27cecfd7b07d1e9a8bdeeb4"},
+ {file = "regex-2025.9.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:6d40e6b49daae9ebbd7fa4e600697372cba85b826592408600068e83a3c47211"},
+ {file = "regex-2025.9.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:0aeb0fe80331059c152a002142699a89bf3e44352aee28261315df0c9874759b"},
+ {file = "regex-2025.9.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:a90014d29cb3098403d82a879105d1418edbbdf948540297435ea6e377023ea7"},
+ {file = "regex-2025.9.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:6ff623271e0b0cc5a95b802666bbd70f17ddd641582d65b10fb260cc0c003529"},
+ {file = "regex-2025.9.1-cp39-cp39-win32.whl", hash = "sha256:d161bfdeabe236290adfd8c7588da7f835d67e9e7bf2945f1e9e120622839ba6"},
+ {file = "regex-2025.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:43ebc77a7dfe36661192afd8d7df5e8be81ec32d2ad0c65b536f66ebfec3dece"},
+ {file = "regex-2025.9.1-cp39-cp39-win_arm64.whl", hash = "sha256:5d74b557cf5554001a869cda60b9a619be307df4d10155894aeaad3ee67c9899"},
+ {file = "regex-2025.9.1.tar.gz", hash = "sha256:88ac07b38d20b54d79e704e38aa3bd2c0f8027432164226bdee201a1c0c9c9ff"},
+]
+
+[[package]]
+name = "requests"
+version = "2.32.5"
+description = "Python HTTP for Humans."
+optional = false
+python-versions = ">=3.9"
+groups = ["main", "benchmarks", "docs"]
+files = [
+ {file = "requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6"},
+ {file = "requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf"},
+]
+
+[package.dependencies]
+certifi = ">=2017.4.17"
+charset_normalizer = ">=2,<4"
+idna = ">=2.5,<4"
+urllib3 = ">=1.21.1,<3"
+
+[package.extras]
+socks = ["PySocks (>=1.5.6,!=1.5.7)"]
+use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
+
+[[package]]
+name = "rich"
+version = "14.1.0"
+description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal"
+optional = false
+python-versions = ">=3.8.0"
+groups = ["dev"]
+files = [
+ {file = "rich-14.1.0-py3-none-any.whl", hash = "sha256:536f5f1785986d6dbdea3c75205c473f970777b4a0d6c6dd1b696aa05a3fa04f"},
+ {file = "rich-14.1.0.tar.gz", hash = "sha256:e497a48b844b0320d45007cdebfeaeed8db2a4f4bcf49f15e455cfc4af11eaa8"},
+]
+
+[package.dependencies]
+markdown-it-py = ">=2.2.0"
+pygments = ">=2.13.0,<3.0.0"
+
+[package.extras]
+jupyter = ["ipywidgets (>=7.5.1,<9)"]
+
+[[package]]
+name = "roman-numerals-py"
+version = "3.1.0"
+description = "Manipulate well-formed Roman numerals"
+optional = false
+python-versions = ">=3.9"
+groups = ["docs"]
+markers = "python_version >= \"3.11\""
+files = [
+ {file = "roman_numerals_py-3.1.0-py3-none-any.whl", hash = "sha256:9da2ad2fb670bcf24e81070ceb3be72f6c11c440d73bd579fbeca1e9f330954c"},
+ {file = "roman_numerals_py-3.1.0.tar.gz", hash = "sha256:be4bf804f083a4ce001b5eb7e3c0862479d10f94c936f6c4e5f250aa5ff5bd2d"},
+]
+
+[package.extras]
+lint = ["mypy (==1.15.0)", "pyright (==1.1.394)", "ruff (==0.9.7)"]
+test = ["pytest (>=8)"]
+
+[[package]]
+name = "rouge"
+version = "1.0.1"
+description = "Full Python ROUGE Score Implementation (not a wrapper)"
+optional = false
+python-versions = "*"
+groups = ["main"]
+files = [
+ {file = "rouge-1.0.1-py3-none-any.whl", hash = "sha256:28d118536e8c774dc47d1d15ec266479b4dd0914c4672ce117d4002789bdc644"},
+ {file = "rouge-1.0.1.tar.gz", hash = "sha256:12b48346ca47d6bcf3c45061f315452b9ccec0620ee895ec85b7efc3d54aae34"},
+]
+
+[package.dependencies]
+six = "*"
+
+[[package]]
+name = "safetensors"
+version = "0.6.2"
+description = ""
+optional = false
+python-versions = ">=3.9"
+groups = ["main"]
+files = [
+ {file = "safetensors-0.6.2-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:9c85ede8ec58f120bad982ec47746981e210492a6db876882aa021446af8ffba"},
+ {file = "safetensors-0.6.2-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:d6675cf4b39c98dbd7d940598028f3742e0375a6b4d4277e76beb0c35f4b843b"},
+ {file = "safetensors-0.6.2-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d2d2b3ce1e2509c68932ca03ab8f20570920cd9754b05063d4368ee52833ecd"},
+ {file = "safetensors-0.6.2-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:93de35a18f46b0f5a6a1f9e26d91b442094f2df02e9fd7acf224cfec4238821a"},
+ {file = "safetensors-0.6.2-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:89a89b505f335640f9120fac65ddeb83e40f1fd081cb8ed88b505bdccec8d0a1"},
+ {file = "safetensors-0.6.2-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fc4d0d0b937e04bdf2ae6f70cd3ad51328635fe0e6214aa1fc811f3b576b3bda"},
+ {file = "safetensors-0.6.2-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8045db2c872db8f4cbe3faa0495932d89c38c899c603f21e9b6486951a5ecb8f"},
+ {file = "safetensors-0.6.2-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:81e67e8bab9878bb568cffbc5f5e655adb38d2418351dc0859ccac158f753e19"},
+ {file = "safetensors-0.6.2-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:b0e4d029ab0a0e0e4fdf142b194514695b1d7d3735503ba700cf36d0fc7136ce"},
+ {file = "safetensors-0.6.2-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:fa48268185c52bfe8771e46325a1e21d317207bcabcb72e65c6e28e9ffeb29c7"},
+ {file = "safetensors-0.6.2-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:d83c20c12c2d2f465997c51b7ecb00e407e5f94d7dec3ea0cc11d86f60d3fde5"},
+ {file = "safetensors-0.6.2-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:d944cea65fad0ead848b6ec2c37cc0b197194bec228f8020054742190e9312ac"},
+ {file = "safetensors-0.6.2-cp38-abi3-win32.whl", hash = "sha256:cab75ca7c064d3911411461151cb69380c9225798a20e712b102edda2542ddb1"},
+ {file = "safetensors-0.6.2-cp38-abi3-win_amd64.whl", hash = "sha256:c7b214870df923cbc1593c3faee16bec59ea462758699bd3fee399d00aac072c"},
+ {file = "safetensors-0.6.2.tar.gz", hash = "sha256:43ff2aa0e6fa2dc3ea5524ac7ad93a9839256b8703761e76e2d0b2a3fa4f15d9"},
+]
+
+[package.extras]
+all = ["safetensors[jax]", "safetensors[numpy]", "safetensors[paddlepaddle]", "safetensors[pinned-tf]", "safetensors[quality]", "safetensors[testing]", "safetensors[torch]"]
+dev = ["safetensors[all]"]
+jax = ["flax (>=0.6.3)", "jax (>=0.3.25)", "jaxlib (>=0.3.25)", "safetensors[numpy]"]
+mlx = ["mlx (>=0.0.9)"]
+numpy = ["numpy (>=1.21.6)"]
+paddlepaddle = ["paddlepaddle (>=2.4.1)", "safetensors[numpy]"]
+pinned-tf = ["safetensors[numpy]", "tensorflow (==2.18.0)"]
+quality = ["ruff"]
+tensorflow = ["safetensors[numpy]", "tensorflow (>=2.11.0)"]
+testing = ["h5py (>=3.7.0)", "huggingface-hub (>=0.12.1)", "hypothesis (>=6.70.2)", "pytest (>=7.2.0)", "pytest-benchmark (>=4.0.0)", "safetensors[numpy]", "setuptools-rust (>=1.5.2)"]
+testingfree = ["huggingface-hub (>=0.12.1)", "hypothesis (>=6.70.2)", "pytest (>=7.2.0)", "pytest-benchmark (>=4.0.0)", "safetensors[numpy]", "setuptools-rust (>=1.5.2)"]
+torch = ["safetensors[numpy]", "torch (>=1.10)"]
+
+[[package]]
+name = "scikit-learn"
+version = "1.6.1"
+description = "A set of python modules for machine learning and data mining"
+optional = false
+python-versions = ">=3.9"
+groups = ["main"]
+markers = "python_version < \"3.11\""
+files = [
+ {file = "scikit_learn-1.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d056391530ccd1e501056160e3c9673b4da4805eb67eb2bdf4e983e1f9c9204e"},
+ {file = "scikit_learn-1.6.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:0c8d036eb937dbb568c6242fa598d551d88fb4399c0344d95c001980ec1c7d36"},
+ {file = "scikit_learn-1.6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8634c4bd21a2a813e0a7e3900464e6d593162a29dd35d25bdf0103b3fce60ed5"},
+ {file = "scikit_learn-1.6.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:775da975a471c4f6f467725dff0ced5c7ac7bda5e9316b260225b48475279a1b"},
+ {file = "scikit_learn-1.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:8a600c31592bd7dab31e1c61b9bbd6dea1b3433e67d264d17ce1017dbdce8002"},
+ {file = "scikit_learn-1.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:72abc587c75234935e97d09aa4913a82f7b03ee0b74111dcc2881cba3c5a7b33"},
+ {file = "scikit_learn-1.6.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:b3b00cdc8f1317b5f33191df1386c0befd16625f49d979fe77a8d44cae82410d"},
+ {file = "scikit_learn-1.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc4765af3386811c3ca21638f63b9cf5ecf66261cc4815c1db3f1e7dc7b79db2"},
+ {file = "scikit_learn-1.6.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:25fc636bdaf1cc2f4a124a116312d837148b5e10872147bdaf4887926b8c03d8"},
+ {file = "scikit_learn-1.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:fa909b1a36e000a03c382aade0bd2063fd5680ff8b8e501660c0f59f021a6415"},
+ {file = "scikit_learn-1.6.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:926f207c804104677af4857b2c609940b743d04c4c35ce0ddc8ff4f053cddc1b"},
+ {file = "scikit_learn-1.6.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:2c2cae262064e6a9b77eee1c8e768fc46aa0b8338c6a8297b9b6759720ec0ff2"},
+ {file = "scikit_learn-1.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1061b7c028a8663fb9a1a1baf9317b64a257fcb036dae5c8752b2abef31d136f"},
+ {file = "scikit_learn-1.6.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e69fab4ebfc9c9b580a7a80111b43d214ab06250f8a7ef590a4edf72464dd86"},
+ {file = "scikit_learn-1.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:70b1d7e85b1c96383f872a519b3375f92f14731e279a7b4c6cfd650cf5dffc52"},
+ {file = "scikit_learn-1.6.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2ffa1e9e25b3d93990e74a4be2c2fc61ee5af85811562f1288d5d055880c4322"},
+ {file = "scikit_learn-1.6.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:dc5cf3d68c5a20ad6d571584c0750ec641cc46aeef1c1507be51300e6003a7e1"},
+ {file = "scikit_learn-1.6.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c06beb2e839ecc641366000ca84f3cf6fa9faa1777e29cf0c04be6e4d096a348"},
+ {file = "scikit_learn-1.6.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e8ca8cb270fee8f1f76fa9bfd5c3507d60c6438bbee5687f81042e2bb98e5a97"},
+ {file = "scikit_learn-1.6.1-cp313-cp313-win_amd64.whl", hash = "sha256:7a1c43c8ec9fde528d664d947dc4c0789be4077a3647f232869f41d9bf50e0fb"},
+ {file = "scikit_learn-1.6.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a17c1dea1d56dcda2fac315712f3651a1fea86565b64b48fa1bc090249cbf236"},
+ {file = "scikit_learn-1.6.1-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:6a7aa5f9908f0f28f4edaa6963c0a6183f1911e63a69aa03782f0d924c830a35"},
+ {file = "scikit_learn-1.6.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0650e730afb87402baa88afbf31c07b84c98272622aaba002559b614600ca691"},
+ {file = "scikit_learn-1.6.1-cp313-cp313t-win_amd64.whl", hash = "sha256:3f59fe08dc03ea158605170eb52b22a105f238a5d512c4470ddeca71feae8e5f"},
+ {file = "scikit_learn-1.6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6849dd3234e87f55dce1db34c89a810b489ead832aaf4d4550b7ea85628be6c1"},
+ {file = "scikit_learn-1.6.1-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:e7be3fa5d2eb9be7d77c3734ff1d599151bb523674be9b834e8da6abe132f44e"},
+ {file = "scikit_learn-1.6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:44a17798172df1d3c1065e8fcf9019183f06c87609b49a124ebdf57ae6cb0107"},
+ {file = "scikit_learn-1.6.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8b7a3b86e411e4bce21186e1c180d792f3d99223dcfa3b4f597ecc92fa1a422"},
+ {file = "scikit_learn-1.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:7a73d457070e3318e32bdb3aa79a8d990474f19035464dfd8bede2883ab5dc3b"},
+ {file = "scikit_learn-1.6.1.tar.gz", hash = "sha256:b4fc2525eca2c69a59260f583c56a7557c6ccdf8deafdba6e060f94c1c59738e"},
+]
+
+[package.dependencies]
+joblib = ">=1.2.0"
+numpy = ">=1.19.5"
+scipy = ">=1.6.0"
+threadpoolctl = ">=3.1.0"
+
+[package.extras]
+benchmark = ["matplotlib (>=3.3.4)", "memory_profiler (>=0.57.0)", "pandas (>=1.1.5)"]
+build = ["cython (>=3.0.10)", "meson-python (>=0.16.0)", "numpy (>=1.19.5)", "scipy (>=1.6.0)"]
+docs = ["Pillow (>=7.1.2)", "matplotlib (>=3.3.4)", "memory_profiler (>=0.57.0)", "numpydoc (>=1.2.0)", "pandas (>=1.1.5)", "plotly (>=5.14.0)", "polars (>=0.20.30)", "pooch (>=1.6.0)", "pydata-sphinx-theme (>=0.15.3)", "scikit-image (>=0.17.2)", "seaborn (>=0.9.0)", "sphinx (>=7.3.7)", "sphinx-copybutton (>=0.5.2)", "sphinx-design (>=0.5.0)", "sphinx-design (>=0.6.0)", "sphinx-gallery (>=0.17.1)", "sphinx-prompt (>=1.4.0)", "sphinx-remove-toctrees (>=1.0.0.post1)", "sphinxcontrib-sass (>=0.3.4)", "sphinxext-opengraph (>=0.9.1)", "towncrier (>=24.8.0)"]
+examples = ["matplotlib (>=3.3.4)", "pandas (>=1.1.5)", "plotly (>=5.14.0)", "pooch (>=1.6.0)", "scikit-image (>=0.17.2)", "seaborn (>=0.9.0)"]
+install = ["joblib (>=1.2.0)", "numpy (>=1.19.5)", "scipy (>=1.6.0)", "threadpoolctl (>=3.1.0)"]
+maintenance = ["conda-lock (==2.5.6)"]
+tests = ["black (>=24.3.0)", "matplotlib (>=3.3.4)", "mypy (>=1.9)", "numpydoc (>=1.2.0)", "pandas (>=1.1.5)", "polars (>=0.20.30)", "pooch (>=1.6.0)", "pyamg (>=4.0.0)", "pyarrow (>=12.0.0)", "pytest (>=7.1.2)", "pytest-cov (>=2.9.0)", "ruff (>=0.5.1)", "scikit-image (>=0.17.2)"]
+
+[[package]]
+name = "scikit-learn"
+version = "1.7.1"
+description = "A set of python modules for machine learning and data mining"
+optional = false
+python-versions = ">=3.10"
+groups = ["main"]
+markers = "python_version >= \"3.11\""
+files = [
+ {file = "scikit_learn-1.7.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:406204dd4004f0517f0b23cf4b28c6245cbd51ab1b6b78153bc784def214946d"},
+ {file = "scikit_learn-1.7.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:16af2e44164f05d04337fd1fc3ae7c4ea61fd9b0d527e22665346336920fe0e1"},
+ {file = "scikit_learn-1.7.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2f2e78e56a40c7587dea9a28dc4a49500fa2ead366869418c66f0fd75b80885c"},
+ {file = "scikit_learn-1.7.1-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b62b76ad408a821475b43b7bb90a9b1c9a4d8d125d505c2df0539f06d6e631b1"},
+ {file = "scikit_learn-1.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:9963b065677a4ce295e8ccdee80a1dd62b37249e667095039adcd5bce6e90deb"},
+ {file = "scikit_learn-1.7.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:90c8494ea23e24c0fb371afc474618c1019dc152ce4a10e4607e62196113851b"},
+ {file = "scikit_learn-1.7.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:bb870c0daf3bf3be145ec51df8ac84720d9972170786601039f024bf6d61a518"},
+ {file = "scikit_learn-1.7.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:40daccd1b5623f39e8943ab39735cadf0bdce80e67cdca2adcb5426e987320a8"},
+ {file = "scikit_learn-1.7.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:30d1f413cfc0aa5a99132a554f1d80517563c34a9d3e7c118fde2d273c6fe0f7"},
+ {file = "scikit_learn-1.7.1-cp311-cp311-win_amd64.whl", hash = "sha256:c711d652829a1805a95d7fe96654604a8f16eab5a9e9ad87b3e60173415cb650"},
+ {file = "scikit_learn-1.7.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3cee419b49b5bbae8796ecd690f97aa412ef1674410c23fc3257c6b8b85b8087"},
+ {file = "scikit_learn-1.7.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:2fd8b8d35817b0d9ebf0b576f7d5ffbbabdb55536b0655a8aaae629d7ffd2e1f"},
+ {file = "scikit_learn-1.7.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:588410fa19a96a69763202f1d6b7b91d5d7a5d73be36e189bc6396bfb355bd87"},
+ {file = "scikit_learn-1.7.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e3142f0abe1ad1d1c31a2ae987621e41f6b578144a911ff4ac94781a583adad7"},
+ {file = "scikit_learn-1.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:3ddd9092c1bd469acab337d87930067c87eac6bd544f8d5027430983f1e1ae88"},
+ {file = "scikit_learn-1.7.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b7839687fa46d02e01035ad775982f2470be2668e13ddd151f0f55a5bf123bae"},
+ {file = "scikit_learn-1.7.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:a10f276639195a96c86aa572ee0698ad64ee939a7b042060b98bd1930c261d10"},
+ {file = "scikit_learn-1.7.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:13679981fdaebc10cc4c13c43344416a86fcbc61449cb3e6517e1df9d12c8309"},
+ {file = "scikit_learn-1.7.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4f1262883c6a63f067a980a8cdd2d2e7f2513dddcef6a9eaada6416a7a7cbe43"},
+ {file = "scikit_learn-1.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:ca6d31fb10e04d50bfd2b50d66744729dbb512d4efd0223b864e2fdbfc4cee11"},
+ {file = "scikit_learn-1.7.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:781674d096303cfe3d351ae6963ff7c958db61cde3421cd490e3a5a58f2a94ae"},
+ {file = "scikit_learn-1.7.1-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:10679f7f125fe7ecd5fad37dd1aa2daae7e3ad8df7f3eefa08901b8254b3e12c"},
+ {file = "scikit_learn-1.7.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1f812729e38c8cb37f760dce71a9b83ccfb04f59b3dca7c6079dcdc60544fa9e"},
+ {file = "scikit_learn-1.7.1-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:88e1a20131cf741b84b89567e1717f27a2ced228e0f29103426102bc2e3b8ef7"},
+ {file = "scikit_learn-1.7.1-cp313-cp313t-win_amd64.whl", hash = "sha256:b1bd1d919210b6a10b7554b717c9000b5485aa95a1d0f177ae0d7ee8ec750da5"},
+ {file = "scikit_learn-1.7.1.tar.gz", hash = "sha256:24b3f1e976a4665aa74ee0fcaac2b8fccc6ae77c8e07ab25da3ba6d3292b9802"},
+]
+
+[package.dependencies]
+joblib = ">=1.2.0"
+numpy = ">=1.22.0"
+scipy = ">=1.8.0"
+threadpoolctl = ">=3.1.0"
+
+[package.extras]
+benchmark = ["matplotlib (>=3.5.0)", "memory_profiler (>=0.57.0)", "pandas (>=1.4.0)"]
+build = ["cython (>=3.0.10)", "meson-python (>=0.17.1)", "numpy (>=1.22.0)", "scipy (>=1.8.0)"]
+docs = ["Pillow (>=8.4.0)", "matplotlib (>=3.5.0)", "memory_profiler (>=0.57.0)", "numpydoc (>=1.2.0)", "pandas (>=1.4.0)", "plotly (>=5.14.0)", "polars (>=0.20.30)", "pooch (>=1.6.0)", "pydata-sphinx-theme (>=0.15.3)", "scikit-image (>=0.19.0)", "seaborn (>=0.9.0)", "sphinx (>=7.3.7)", "sphinx-copybutton (>=0.5.2)", "sphinx-design (>=0.5.0)", "sphinx-design (>=0.6.0)", "sphinx-gallery (>=0.17.1)", "sphinx-prompt (>=1.4.0)", "sphinx-remove-toctrees (>=1.0.0.post1)", "sphinxcontrib-sass (>=0.3.4)", "sphinxext-opengraph (>=0.9.1)", "towncrier (>=24.8.0)"]
+examples = ["matplotlib (>=3.5.0)", "pandas (>=1.4.0)", "plotly (>=5.14.0)", "pooch (>=1.6.0)", "scikit-image (>=0.19.0)", "seaborn (>=0.9.0)"]
+install = ["joblib (>=1.2.0)", "numpy (>=1.22.0)", "scipy (>=1.8.0)", "threadpoolctl (>=3.1.0)"]
+maintenance = ["conda-lock (==3.0.1)"]
+tests = ["matplotlib (>=3.5.0)", "mypy (>=1.15)", "numpydoc (>=1.2.0)", "pandas (>=1.4.0)", "polars (>=0.20.30)", "pooch (>=1.6.0)", "pyamg (>=4.2.1)", "pyarrow (>=12.0.0)", "pytest (>=7.1.2)", "pytest-cov (>=2.9.0)", "ruff (>=0.11.7)", "scikit-image (>=0.19.0)"]
+
+[[package]]
+name = "scipy"
+version = "1.13.1"
+description = "Fundamental algorithms for scientific computing in Python"
+optional = false
+python-versions = ">=3.9"
+groups = ["main"]
+markers = "python_version < \"3.11\""
+files = [
+ {file = "scipy-1.13.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:20335853b85e9a49ff7572ab453794298bcf0354d8068c5f6775a0eabf350aca"},
+ {file = "scipy-1.13.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:d605e9c23906d1994f55ace80e0125c587f96c020037ea6aa98d01b4bd2e222f"},
+ {file = "scipy-1.13.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cfa31f1def5c819b19ecc3a8b52d28ffdcc7ed52bb20c9a7589669dd3c250989"},
+ {file = "scipy-1.13.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f26264b282b9da0952a024ae34710c2aff7d27480ee91a2e82b7b7073c24722f"},
+ {file = "scipy-1.13.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:eccfa1906eacc02de42d70ef4aecea45415f5be17e72b61bafcfd329bdc52e94"},
+ {file = "scipy-1.13.1-cp310-cp310-win_amd64.whl", hash = "sha256:2831f0dc9c5ea9edd6e51e6e769b655f08ec6db6e2e10f86ef39bd32eb11da54"},
+ {file = "scipy-1.13.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:27e52b09c0d3a1d5b63e1105f24177e544a222b43611aaf5bc44d4a0979e32f9"},
+ {file = "scipy-1.13.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:54f430b00f0133e2224c3ba42b805bfd0086fe488835effa33fa291561932326"},
+ {file = "scipy-1.13.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e89369d27f9e7b0884ae559a3a956e77c02114cc60a6058b4e5011572eea9299"},
+ {file = "scipy-1.13.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a78b4b3345f1b6f68a763c6e25c0c9a23a9fd0f39f5f3d200efe8feda560a5fa"},
+ {file = "scipy-1.13.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:45484bee6d65633752c490404513b9ef02475b4284c4cfab0ef946def50b3f59"},
+ {file = "scipy-1.13.1-cp311-cp311-win_amd64.whl", hash = "sha256:5713f62f781eebd8d597eb3f88b8bf9274e79eeabf63afb4a737abc6c84ad37b"},
+ {file = "scipy-1.13.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5d72782f39716b2b3509cd7c33cdc08c96f2f4d2b06d51e52fb45a19ca0c86a1"},
+ {file = "scipy-1.13.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:017367484ce5498445aade74b1d5ab377acdc65e27095155e448c88497755a5d"},
+ {file = "scipy-1.13.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:949ae67db5fa78a86e8fa644b9a6b07252f449dcf74247108c50e1d20d2b4627"},
+ {file = "scipy-1.13.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de3ade0e53bc1f21358aa74ff4830235d716211d7d077e340c7349bc3542e884"},
+ {file = "scipy-1.13.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2ac65fb503dad64218c228e2dc2d0a0193f7904747db43014645ae139c8fad16"},
+ {file = "scipy-1.13.1-cp312-cp312-win_amd64.whl", hash = "sha256:cdd7dacfb95fea358916410ec61bbc20440f7860333aee6d882bb8046264e949"},
+ {file = "scipy-1.13.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:436bbb42a94a8aeef855d755ce5a465479c721e9d684de76bf61a62e7c2b81d5"},
+ {file = "scipy-1.13.1-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:8335549ebbca860c52bf3d02f80784e91a004b71b059e3eea9678ba994796a24"},
+ {file = "scipy-1.13.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d533654b7d221a6a97304ab63c41c96473ff04459e404b83275b60aa8f4b7004"},
+ {file = "scipy-1.13.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:637e98dcf185ba7f8e663e122ebf908c4702420477ae52a04f9908707456ba4d"},
+ {file = "scipy-1.13.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a014c2b3697bde71724244f63de2476925596c24285c7a637364761f8710891c"},
+ {file = "scipy-1.13.1-cp39-cp39-win_amd64.whl", hash = "sha256:392e4ec766654852c25ebad4f64e4e584cf19820b980bc04960bca0b0cd6eaa2"},
+ {file = "scipy-1.13.1.tar.gz", hash = "sha256:095a87a0312b08dfd6a6155cbbd310a8c51800fc931b8c0b84003014b874ed3c"},
+]
+
+[package.dependencies]
+numpy = ">=1.22.4,<2.3"
+
+[package.extras]
+dev = ["cython-lint (>=0.12.2)", "doit (>=0.36.0)", "mypy", "pycodestyle", "pydevtool", "rich-click", "ruff", "types-psutil", "typing_extensions"]
+doc = ["jupyterlite-pyodide-kernel", "jupyterlite-sphinx (>=0.12.0)", "jupytext", "matplotlib (>=3.5)", "myst-nb", "numpydoc", "pooch", "pydata-sphinx-theme (>=0.15.2)", "sphinx (>=5.0.0)", "sphinx-design (>=0.4.0)"]
+test = ["array-api-strict", "asv", "gmpy2", "hypothesis (>=6.30)", "mpmath", "pooch", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "scikit-umfpack", "threadpoolctl"]
+
+[[package]]
+name = "scipy"
+version = "1.16.1"
+description = "Fundamental algorithms for scientific computing in Python"
+optional = false
+python-versions = ">=3.11"
+groups = ["main"]
+markers = "python_version >= \"3.11\""
+files = [
+ {file = "scipy-1.16.1-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:c033fa32bab91dc98ca59d0cf23bb876454e2bb02cbe592d5023138778f70030"},
+ {file = "scipy-1.16.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:6e5c2f74e5df33479b5cd4e97a9104c511518fbd979aa9b8f6aec18b2e9ecae7"},
+ {file = "scipy-1.16.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:0a55ffe0ba0f59666e90951971a884d1ff6f4ec3275a48f472cfb64175570f77"},
+ {file = "scipy-1.16.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:f8a5d6cd147acecc2603fbd382fed6c46f474cccfcf69ea32582e033fb54dcfe"},
+ {file = "scipy-1.16.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cb18899127278058bcc09e7b9966d41a5a43740b5bb8dcba401bd983f82e885b"},
+ {file = "scipy-1.16.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:adccd93a2fa937a27aae826d33e3bfa5edf9aa672376a4852d23a7cd67a2e5b7"},
+ {file = "scipy-1.16.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:18aca1646a29ee9a0625a1be5637fa798d4d81fdf426481f06d69af828f16958"},
+ {file = "scipy-1.16.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d85495cef541729a70cdddbbf3e6b903421bc1af3e8e3a9a72a06751f33b7c39"},
+ {file = "scipy-1.16.1-cp311-cp311-win_amd64.whl", hash = "sha256:226652fca853008119c03a8ce71ffe1b3f6d2844cc1686e8f9806edafae68596"},
+ {file = "scipy-1.16.1-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:81b433bbeaf35728dad619afc002db9b189e45eebe2cd676effe1fb93fef2b9c"},
+ {file = "scipy-1.16.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:886cc81fdb4c6903a3bb0464047c25a6d1016fef77bb97949817d0c0d79f9e04"},
+ {file = "scipy-1.16.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:15240c3aac087a522b4eaedb09f0ad061753c5eebf1ea430859e5bf8640d5919"},
+ {file = "scipy-1.16.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:65f81a25805f3659b48126b5053d9e823d3215e4a63730b5e1671852a1705921"},
+ {file = "scipy-1.16.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:6c62eea7f607f122069b9bad3f99489ddca1a5173bef8a0c75555d7488b6f725"},
+ {file = "scipy-1.16.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f965bbf3235b01c776115ab18f092a95aa74c271a52577bcb0563e85738fd618"},
+ {file = "scipy-1.16.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f006e323874ffd0b0b816d8c6a8e7f9a73d55ab3b8c3f72b752b226d0e3ac83d"},
+ {file = "scipy-1.16.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e8fd15fc5085ab4cca74cb91fe0a4263b1f32e4420761ddae531ad60934c2119"},
+ {file = "scipy-1.16.1-cp312-cp312-win_amd64.whl", hash = "sha256:f7b8013c6c066609577d910d1a2a077021727af07b6fab0ee22c2f901f22352a"},
+ {file = "scipy-1.16.1-cp313-cp313-macosx_10_14_x86_64.whl", hash = "sha256:5451606823a5e73dfa621a89948096c6528e2896e40b39248295d3a0138d594f"},
+ {file = "scipy-1.16.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:89728678c5ca5abd610aee148c199ac1afb16e19844401ca97d43dc548a354eb"},
+ {file = "scipy-1.16.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:e756d688cb03fd07de0fffad475649b03cb89bee696c98ce508b17c11a03f95c"},
+ {file = "scipy-1.16.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:5aa2687b9935da3ed89c5dbed5234576589dd28d0bf7cd237501ccfbdf1ad608"},
+ {file = "scipy-1.16.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0851f6a1e537fe9399f35986897e395a1aa61c574b178c0d456be5b1a0f5ca1f"},
+ {file = "scipy-1.16.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fedc2cbd1baed37474b1924c331b97bdff611d762c196fac1a9b71e67b813b1b"},
+ {file = "scipy-1.16.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2ef500e72f9623a6735769e4b93e9dcb158d40752cdbb077f305487e3e2d1f45"},
+ {file = "scipy-1.16.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:978d8311674b05a8f7ff2ea6c6bce5d8b45a0cb09d4c5793e0318f448613ea65"},
+ {file = "scipy-1.16.1-cp313-cp313-win_amd64.whl", hash = "sha256:81929ed0fa7a5713fcdd8b2e6f73697d3b4c4816d090dd34ff937c20fa90e8ab"},
+ {file = "scipy-1.16.1-cp313-cp313t-macosx_10_14_x86_64.whl", hash = "sha256:bcc12db731858abda693cecdb3bdc9e6d4bd200213f49d224fe22df82687bdd6"},
+ {file = "scipy-1.16.1-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:744d977daa4becb9fc59135e75c069f8d301a87d64f88f1e602a9ecf51e77b27"},
+ {file = "scipy-1.16.1-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:dc54f76ac18073bcecffb98d93f03ed6b81a92ef91b5d3b135dcc81d55a724c7"},
+ {file = "scipy-1.16.1-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:367d567ee9fc1e9e2047d31f39d9d6a7a04e0710c86e701e053f237d14a9b4f6"},
+ {file = "scipy-1.16.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4cf5785e44e19dcd32a0e4807555e1e9a9b8d475c6afff3d21c3c543a6aa84f4"},
+ {file = "scipy-1.16.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3d0b80fb26d3e13a794c71d4b837e2a589d839fd574a6bbb4ee1288c213ad4a3"},
+ {file = "scipy-1.16.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:8503517c44c18d1030d666cb70aaac1cc8913608816e06742498833b128488b7"},
+ {file = "scipy-1.16.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:30cc4bb81c41831ecfd6dc450baf48ffd80ef5aed0f5cf3ea775740e80f16ecc"},
+ {file = "scipy-1.16.1-cp313-cp313t-win_amd64.whl", hash = "sha256:c24fa02f7ed23ae514460a22c57eca8f530dbfa50b1cfdbf4f37c05b5309cc39"},
+ {file = "scipy-1.16.1-cp314-cp314-macosx_10_14_x86_64.whl", hash = "sha256:796a5a9ad36fa3a782375db8f4241ab02a091308eb079746bc0f874c9b998318"},
+ {file = "scipy-1.16.1-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:3ea0733a2ff73fd6fdc5fecca54ee9b459f4d74f00b99aced7d9a3adb43fb1cc"},
+ {file = "scipy-1.16.1-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:85764fb15a2ad994e708258bb4ed8290d1305c62a4e1ef07c414356a24fcfbf8"},
+ {file = "scipy-1.16.1-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:ca66d980469cb623b1759bdd6e9fd97d4e33a9fad5b33771ced24d0cb24df67e"},
+ {file = "scipy-1.16.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e7cc1ffcc230f568549fc56670bcf3df1884c30bd652c5da8138199c8c76dae0"},
+ {file = "scipy-1.16.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3ddfb1e8d0b540cb4ee9c53fc3dea3186f97711248fb94b4142a1b27178d8b4b"},
+ {file = "scipy-1.16.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4dc0e7be79e95d8ba3435d193e0d8ce372f47f774cffd882f88ea4e1e1ddc731"},
+ {file = "scipy-1.16.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f23634f9e5adb51b2a77766dac217063e764337fbc816aa8ad9aaebcd4397fd3"},
+ {file = "scipy-1.16.1-cp314-cp314-win_amd64.whl", hash = "sha256:57d75524cb1c5a374958a2eae3d84e1929bb971204cc9d52213fb8589183fc19"},
+ {file = "scipy-1.16.1-cp314-cp314t-macosx_10_14_x86_64.whl", hash = "sha256:d8da7c3dd67bcd93f15618938f43ed0995982eb38973023d46d4646c4283ad65"},
+ {file = "scipy-1.16.1-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:cc1d2f2fd48ba1e0620554fe5bc44d3e8f5d4185c8c109c7fbdf5af2792cfad2"},
+ {file = "scipy-1.16.1-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:21a611ced9275cb861bacadbada0b8c0623bc00b05b09eb97f23b370fc2ae56d"},
+ {file = "scipy-1.16.1-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:8dfbb25dffc4c3dd9371d8ab456ca81beeaf6f9e1c2119f179392f0dc1ab7695"},
+ {file = "scipy-1.16.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f0ebb7204f063fad87fc0a0e4ff4a2ff40b2a226e4ba1b7e34bf4b79bf97cd86"},
+ {file = "scipy-1.16.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f1b9e5962656f2734c2b285a8745358ecb4e4efbadd00208c80a389227ec61ff"},
+ {file = "scipy-1.16.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5e1a106f8c023d57a2a903e771228bf5c5b27b5d692088f457acacd3b54511e4"},
+ {file = "scipy-1.16.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:709559a1db68a9abc3b2c8672c4badf1614f3b440b3ab326d86a5c0491eafae3"},
+ {file = "scipy-1.16.1-cp314-cp314t-win_amd64.whl", hash = "sha256:c0c804d60492a0aad7f5b2bb1862f4548b990049e27e828391ff2bf6f7199998"},
+ {file = "scipy-1.16.1.tar.gz", hash = "sha256:44c76f9e8b6e8e488a586190ab38016e4ed2f8a038af7cd3defa903c0a2238b3"},
+]
+
+[package.dependencies]
+numpy = ">=1.25.2,<2.6"
+
+[package.extras]
+dev = ["cython-lint (>=0.12.2)", "doit (>=0.36.0)", "mypy (==1.10.0)", "pycodestyle", "pydevtool", "rich-click", "ruff (>=0.0.292)", "types-psutil", "typing_extensions"]
+doc = ["intersphinx_registry", "jupyterlite-pyodide-kernel", "jupyterlite-sphinx (>=0.19.1)", "jupytext", "linkify-it-py", "matplotlib (>=3.5)", "myst-nb (>=1.2.0)", "numpydoc", "pooch", "pydata-sphinx-theme (>=0.15.2)", "sphinx (>=5.0.0,<8.2.0)", "sphinx-copybutton", "sphinx-design (>=0.4.0)"]
+test = ["Cython", "array-api-strict (>=2.3.1)", "asv", "gmpy2", "hypothesis (>=6.30)", "meson", "mpmath", "ninja ; sys_platform != \"emscripten\"", "pooch", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "scikit-umfpack", "threadpoolctl"]
+
+[[package]]
+name = "seaborn"
+version = "0.13.2"
+description = "Statistical data visualization"
+optional = false
+python-versions = ">=3.8"
+groups = ["main"]
+files = [
+ {file = "seaborn-0.13.2-py3-none-any.whl", hash = "sha256:636f8336facf092165e27924f223d3c62ca560b1f2bb5dff7ab7fad265361987"},
+ {file = "seaborn-0.13.2.tar.gz", hash = "sha256:93e60a40988f4d65e9f4885df477e2fdaff6b73a9ded434c1ab356dd57eefff7"},
+]
+
+[package.dependencies]
+matplotlib = ">=3.4,<3.6.1 || >3.6.1"
+numpy = ">=1.20,<1.24.0 || >1.24.0"
+pandas = ">=1.2"
+
+[package.extras]
+dev = ["flake8", "flit", "mypy", "pandas-stubs", "pre-commit", "pytest", "pytest-cov", "pytest-xdist"]
+docs = ["ipykernel", "nbconvert", "numpydoc", "pydata_sphinx_theme (==0.10.0rc2)", "pyyaml", "sphinx (<6.0.0)", "sphinx-copybutton", "sphinx-design", "sphinx-issues"]
+stats = ["scipy (>=1.7)", "statsmodels (>=0.12)"]
+
+[[package]]
+name = "sentry-sdk"
+version = "2.35.2"
+description = "Python client for Sentry (https://sentry.io)"
+optional = false
+python-versions = ">=3.6"
+groups = ["benchmarks"]
+files = [
+ {file = "sentry_sdk-2.35.2-py2.py3-none-any.whl", hash = "sha256:38c98e3cbb620dd3dd80a8d6e39c753d453dd41f8a9df581b0584c19a52bc926"},
+ {file = "sentry_sdk-2.35.2.tar.gz", hash = "sha256:e9e8f3c795044beb59f2c8f4c6b9b0f9779e5e604099882df05eec525e782cc6"},
+]
+
+[package.dependencies]
+certifi = "*"
+urllib3 = ">=1.26.11"
+
+[package.extras]
+aiohttp = ["aiohttp (>=3.5)"]
+anthropic = ["anthropic (>=0.16)"]
+arq = ["arq (>=0.23)"]
+asyncpg = ["asyncpg (>=0.23)"]
+beam = ["apache-beam (>=2.12)"]
+bottle = ["bottle (>=0.12.13)"]
+celery = ["celery (>=3)"]
+celery-redbeat = ["celery-redbeat (>=2)"]
+chalice = ["chalice (>=1.16.0)"]
+clickhouse-driver = ["clickhouse-driver (>=0.2.0)"]
+django = ["django (>=1.8)"]
+falcon = ["falcon (>=1.4)"]
+fastapi = ["fastapi (>=0.79.0)"]
+flask = ["blinker (>=1.1)", "flask (>=0.11)", "markupsafe"]
+grpcio = ["grpcio (>=1.21.1)", "protobuf (>=3.8.0)"]
+http2 = ["httpcore[http2] (==1.*)"]
+httpx = ["httpx (>=0.16.0)"]
+huey = ["huey (>=2)"]
+huggingface-hub = ["huggingface_hub (>=0.22)"]
+langchain = ["langchain (>=0.0.210)"]
+launchdarkly = ["launchdarkly-server-sdk (>=9.8.0)"]
+litestar = ["litestar (>=2.0.0)"]
+loguru = ["loguru (>=0.5)"]
+openai = ["openai (>=1.0.0)", "tiktoken (>=0.3.0)"]
+openfeature = ["openfeature-sdk (>=0.7.1)"]
+opentelemetry = ["opentelemetry-distro (>=0.35b0)"]
+opentelemetry-experimental = ["opentelemetry-distro"]
+pure-eval = ["asttokens", "executing", "pure_eval"]
+pymongo = ["pymongo (>=3.1)"]
+pyspark = ["pyspark (>=2.4.4)"]
+quart = ["blinker (>=1.1)", "quart (>=0.16.1)"]
+rq = ["rq (>=0.6)"]
+sanic = ["sanic (>=0.8)"]
+sqlalchemy = ["sqlalchemy (>=1.2)"]
+starlette = ["starlette (>=0.19.1)"]
+starlite = ["starlite (>=1.48)"]
+statsig = ["statsig (>=0.55.3)"]
+tornado = ["tornado (>=6)"]
+unleash = ["UnleashClient (>=6.0.1)"]
+
+[[package]]
+name = "setuptools"
+version = "80.9.0"
+description = "Easily download, build, install, upgrade, and uninstall Python packages"
+optional = false
+python-versions = ">=3.9"
+groups = ["main"]
+markers = "(platform_machine == \"x86_64\" or python_version >= \"3.12\") and (platform_system == \"Linux\" or python_version >= \"3.12\")"
+files = [
+ {file = "setuptools-80.9.0-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922"},
+ {file = "setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c"},
+]
+
+[package.extras]
+check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\"", "ruff (>=0.8.0) ; sys_platform != \"cygwin\""]
+core = ["importlib_metadata (>=6) ; python_version < \"3.10\"", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more_itertools", "more_itertools (>=8.8)", "packaging (>=24.2)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1) ; python_version < \"3.11\"", "wheel (>=0.43.0)"]
+cover = ["pytest-cov"]
+doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"]
+enabler = ["pytest-enabler (>=2.2)"]
+test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21) ; python_version >= \"3.9\" and sys_platform != \"cygwin\"", "jaraco.envs (>=2.2)", "jaraco.path (>=3.7.2)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf ; sys_platform != \"cygwin\"", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"]
+type = ["importlib_metadata (>=7.0.2) ; python_version < \"3.10\"", "jaraco.develop (>=7.21) ; sys_platform != \"cygwin\"", "mypy (==1.14.*)", "pytest-mypy"]
+
+[[package]]
+name = "six"
+version = "1.17.0"
+description = "Python 2 and 3 compatibility utilities"
+optional = false
+python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
+groups = ["main"]
+files = [
+ {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"},
+ {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"},
+]
+
+[[package]]
+name = "smmap"
+version = "5.0.2"
+description = "A pure Python implementation of a sliding window memory map manager"
+optional = false
+python-versions = ">=3.7"
+groups = ["benchmarks"]
+files = [
+ {file = "smmap-5.0.2-py3-none-any.whl", hash = "sha256:b30115f0def7d7531d22a0fb6502488d879e75b260a9db4d0819cfb25403af5e"},
+ {file = "smmap-5.0.2.tar.gz", hash = "sha256:26ea65a03958fa0c8a1c7e8c7a58fdc77221b8910f6be2131affade476898ad5"},
+]
+
+[[package]]
+name = "snowballstemmer"
+version = "3.0.1"
+description = "This package provides 32 stemmers for 30 languages generated from Snowball algorithms."
+optional = false
+python-versions = "!=3.0.*,!=3.1.*,!=3.2.*"
+groups = ["docs"]
+files = [
+ {file = "snowballstemmer-3.0.1-py3-none-any.whl", hash = "sha256:6cd7b3897da8d6c9ffb968a6781fa6532dce9c3618a4b127d920dab764a19064"},
+ {file = "snowballstemmer-3.0.1.tar.gz", hash = "sha256:6d5eeeec8e9f84d4d56b847692bacf79bc2c8e90c7f80ca4444ff8b6f2e52895"},
+]
+
+[[package]]
+name = "sphinx"
+version = "7.4.7"
+description = "Python documentation generator"
+optional = false
+python-versions = ">=3.9"
+groups = ["docs"]
+markers = "python_version < \"3.11\""
+files = [
+ {file = "sphinx-7.4.7-py3-none-any.whl", hash = "sha256:c2419e2135d11f1951cd994d6eb18a1835bd8fdd8429f9ca375dc1f3281bd239"},
+ {file = "sphinx-7.4.7.tar.gz", hash = "sha256:242f92a7ea7e6c5b406fdc2615413890ba9f699114a9c09192d7dfead2ee9cfe"},
+]
+
+[package.dependencies]
+alabaster = ">=0.7.14,<0.8.0"
+babel = ">=2.13"
+colorama = {version = ">=0.4.6", markers = "sys_platform == \"win32\""}
+docutils = ">=0.20,<0.22"
+imagesize = ">=1.3"
+importlib-metadata = {version = ">=6.0", markers = "python_version < \"3.10\""}
+Jinja2 = ">=3.1"
+packaging = ">=23.0"
+Pygments = ">=2.17"
+requests = ">=2.30.0"
+snowballstemmer = ">=2.2"
+sphinxcontrib-applehelp = "*"
+sphinxcontrib-devhelp = "*"
+sphinxcontrib-htmlhelp = ">=2.0.0"
+sphinxcontrib-jsmath = "*"
+sphinxcontrib-qthelp = "*"
+sphinxcontrib-serializinghtml = ">=1.1.9"
+tomli = {version = ">=2", markers = "python_version < \"3.11\""}
+
+[package.extras]
+docs = ["sphinxcontrib-websupport"]
+lint = ["flake8 (>=6.0)", "importlib-metadata (>=6.0)", "mypy (==1.10.1)", "pytest (>=6.0)", "ruff (==0.5.2)", "sphinx-lint (>=0.9)", "tomli (>=2)", "types-docutils (==0.21.0.20240711)", "types-requests (>=2.30.0)"]
+test = ["cython (>=3.0)", "defusedxml (>=0.7.1)", "pytest (>=8.0)", "setuptools (>=70.0)", "typing_extensions (>=4.9)"]
+
+[[package]]
+name = "sphinx"
+version = "8.2.3"
+description = "Python documentation generator"
+optional = false
+python-versions = ">=3.11"
+groups = ["docs"]
+markers = "python_version >= \"3.11\""
+files = [
+ {file = "sphinx-8.2.3-py3-none-any.whl", hash = "sha256:4405915165f13521d875a8c29c8970800a0141c14cc5416a38feca4ea5d9b9c3"},
+ {file = "sphinx-8.2.3.tar.gz", hash = "sha256:398ad29dee7f63a75888314e9424d40f52ce5a6a87ae88e7071e80af296ec348"},
+]
+
+[package.dependencies]
+alabaster = ">=0.7.14"
+babel = ">=2.13"
+colorama = {version = ">=0.4.6", markers = "sys_platform == \"win32\""}
+docutils = ">=0.20,<0.22"
+imagesize = ">=1.3"
+Jinja2 = ">=3.1"
+packaging = ">=23.0"
+Pygments = ">=2.17"
+requests = ">=2.30.0"
+roman-numerals-py = ">=1.0.0"
+snowballstemmer = ">=2.2"
+sphinxcontrib-applehelp = ">=1.0.7"
+sphinxcontrib-devhelp = ">=1.0.6"
+sphinxcontrib-htmlhelp = ">=2.0.6"
+sphinxcontrib-jsmath = ">=1.0.1"
+sphinxcontrib-qthelp = ">=1.0.6"
+sphinxcontrib-serializinghtml = ">=1.1.9"
+
+[package.extras]
+docs = ["sphinxcontrib-websupport"]
+lint = ["betterproto (==2.0.0b6)", "mypy (==1.15.0)", "pypi-attestations (==0.0.21)", "pyright (==1.1.395)", "pytest (>=8.0)", "ruff (==0.9.9)", "sphinx-lint (>=0.9)", "types-Pillow (==10.2.0.20240822)", "types-Pygments (==2.19.0.20250219)", "types-colorama (==0.4.15.20240311)", "types-defusedxml (==0.7.0.20240218)", "types-docutils (==0.21.0.20241128)", "types-requests (==2.32.0.20241016)", "types-urllib3 (==1.26.25.14)"]
+test = ["cython (>=3.0)", "defusedxml (>=0.7.1)", "pytest (>=8.0)", "pytest-xdist[psutil] (>=3.4)", "setuptools (>=70.0)", "typing_extensions (>=4.9)"]
+
+[[package]]
+name = "sphinx-rtd-theme"
+version = "3.0.2"
+description = "Read the Docs theme for Sphinx"
+optional = false
+python-versions = ">=3.8"
+groups = ["docs"]
+files = [
+ {file = "sphinx_rtd_theme-3.0.2-py2.py3-none-any.whl", hash = "sha256:422ccc750c3a3a311de4ae327e82affdaf59eb695ba4936538552f3b00f4ee13"},
+ {file = "sphinx_rtd_theme-3.0.2.tar.gz", hash = "sha256:b7457bc25dda723b20b086a670b9953c859eab60a2a03ee8eb2bb23e176e5f85"},
+]
+
+[package.dependencies]
+docutils = ">0.18,<0.22"
+sphinx = ">=6,<9"
+sphinxcontrib-jquery = ">=4,<5"
+
+[package.extras]
+dev = ["bump2version", "transifex-client", "twine", "wheel"]
+
+[[package]]
+name = "sphinxcontrib-applehelp"
+version = "2.0.0"
+description = "sphinxcontrib-applehelp is a Sphinx extension which outputs Apple help books"
+optional = false
+python-versions = ">=3.9"
+groups = ["docs"]
+files = [
+ {file = "sphinxcontrib_applehelp-2.0.0-py3-none-any.whl", hash = "sha256:4cd3f0ec4ac5dd9c17ec65e9ab272c9b867ea77425228e68ecf08d6b28ddbdb5"},
+ {file = "sphinxcontrib_applehelp-2.0.0.tar.gz", hash = "sha256:2f29ef331735ce958efa4734873f084941970894c6090408b079c61b2e1c06d1"},
+]
+
+[package.extras]
+lint = ["mypy", "ruff (==0.5.5)", "types-docutils"]
+standalone = ["Sphinx (>=5)"]
+test = ["pytest"]
+
+[[package]]
+name = "sphinxcontrib-devhelp"
+version = "2.0.0"
+description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp documents"
+optional = false
+python-versions = ">=3.9"
+groups = ["docs"]
+files = [
+ {file = "sphinxcontrib_devhelp-2.0.0-py3-none-any.whl", hash = "sha256:aefb8b83854e4b0998877524d1029fd3e6879210422ee3780459e28a1f03a8a2"},
+ {file = "sphinxcontrib_devhelp-2.0.0.tar.gz", hash = "sha256:411f5d96d445d1d73bb5d52133377b4248ec79db5c793ce7dbe59e074b4dd1ad"},
+]
+
+[package.extras]
+lint = ["mypy", "ruff (==0.5.5)", "types-docutils"]
+standalone = ["Sphinx (>=5)"]
+test = ["pytest"]
+
+[[package]]
+name = "sphinxcontrib-htmlhelp"
+version = "2.1.0"
+description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files"
+optional = false
+python-versions = ">=3.9"
+groups = ["docs"]
+files = [
+ {file = "sphinxcontrib_htmlhelp-2.1.0-py3-none-any.whl", hash = "sha256:166759820b47002d22914d64a075ce08f4c46818e17cfc9470a9786b759b19f8"},
+ {file = "sphinxcontrib_htmlhelp-2.1.0.tar.gz", hash = "sha256:c9e2916ace8aad64cc13a0d233ee22317f2b9025b9cf3295249fa985cc7082e9"},
+]
+
+[package.extras]
+lint = ["mypy", "ruff (==0.5.5)", "types-docutils"]
+standalone = ["Sphinx (>=5)"]
+test = ["html5lib", "pytest"]
+
+[[package]]
+name = "sphinxcontrib-jquery"
+version = "4.1"
+description = "Extension to include jQuery on newer Sphinx releases"
+optional = false
+python-versions = ">=2.7"
+groups = ["docs"]
+files = [
+ {file = "sphinxcontrib-jquery-4.1.tar.gz", hash = "sha256:1620739f04e36a2c779f1a131a2dfd49b2fd07351bf1968ced074365933abc7a"},
+ {file = "sphinxcontrib_jquery-4.1-py2.py3-none-any.whl", hash = "sha256:f936030d7d0147dd026a4f2b5a57343d233f1fc7b363f68b3d4f1cb0993878ae"},
+]
+
+[package.dependencies]
+Sphinx = ">=1.8"
+
+[[package]]
+name = "sphinxcontrib-jsmath"
+version = "1.0.1"
+description = "A sphinx extension which renders display math in HTML via JavaScript"
+optional = false
+python-versions = ">=3.5"
+groups = ["docs"]
+files = [
+ {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"},
+ {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"},
+]
+
+[package.extras]
+test = ["flake8", "mypy", "pytest"]
+
+[[package]]
+name = "sphinxcontrib-qthelp"
+version = "2.0.0"
+description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp documents"
+optional = false
+python-versions = ">=3.9"
+groups = ["docs"]
+files = [
+ {file = "sphinxcontrib_qthelp-2.0.0-py3-none-any.whl", hash = "sha256:b18a828cdba941ccd6ee8445dbe72ffa3ef8cbe7505d8cd1fa0d42d3f2d5f3eb"},
+ {file = "sphinxcontrib_qthelp-2.0.0.tar.gz", hash = "sha256:4fe7d0ac8fc171045be623aba3e2a8f613f8682731f9153bb2e40ece16b9bbab"},
+]
+
+[package.extras]
+lint = ["mypy", "ruff (==0.5.5)", "types-docutils"]
+standalone = ["Sphinx (>=5)"]
+test = ["defusedxml (>=0.7.1)", "pytest"]
+
+[[package]]
+name = "sphinxcontrib-serializinghtml"
+version = "2.0.0"
+description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)"
+optional = false
+python-versions = ">=3.9"
+groups = ["docs"]
+files = [
+ {file = "sphinxcontrib_serializinghtml-2.0.0-py3-none-any.whl", hash = "sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331"},
+ {file = "sphinxcontrib_serializinghtml-2.0.0.tar.gz", hash = "sha256:e9d912827f872c029017a53f0ef2180b327c3f7fd23c87229f7a8e8b70031d4d"},
+]
+
+[package.extras]
+lint = ["mypy", "ruff (==0.5.5)", "types-docutils"]
+standalone = ["Sphinx (>=5)"]
+test = ["pytest"]
+
+[[package]]
+name = "stevedore"
+version = "5.5.0"
+description = "Manage dynamic plugins for Python applications"
+optional = false
+python-versions = ">=3.9"
+groups = ["dev"]
+files = [
+ {file = "stevedore-5.5.0-py3-none-any.whl", hash = "sha256:18363d4d268181e8e8452e71a38cd77630f345b2ef6b4a8d5614dac5ee0d18cf"},
+ {file = "stevedore-5.5.0.tar.gz", hash = "sha256:d31496a4f4df9825e1a1e4f1f74d19abb0154aff311c3b376fcc89dae8fccd73"},
+]
+
+[[package]]
+name = "sympy"
+version = "1.14.0"
+description = "Computer algebra system (CAS) in Python"
+optional = false
+python-versions = ">=3.9"
+groups = ["main"]
+files = [
+ {file = "sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5"},
+ {file = "sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517"},
+]
+
+[package.dependencies]
+mpmath = ">=1.1.0,<1.4"
+
+[package.extras]
+dev = ["hypothesis (>=6.70.0)", "pytest (>=7.1.0)"]
+
+[[package]]
+name = "threadpoolctl"
+version = "3.6.0"
+description = "threadpoolctl"
+optional = false
+python-versions = ">=3.9"
+groups = ["main"]
+files = [
+ {file = "threadpoolctl-3.6.0-py3-none-any.whl", hash = "sha256:43a0b8fd5a2928500110039e43a5eed8480b918967083ea48dc3ab9f13c4a7fb"},
+ {file = "threadpoolctl-3.6.0.tar.gz", hash = "sha256:8ab8b4aa3491d812b623328249fab5302a68d2d71745c8a4c719a2fcaba9f44e"},
+]
+
+[[package]]
+name = "tokenize-rt"
+version = "6.2.0"
+description = "A wrapper around the stdlib `tokenize` which roundtrips."
+optional = false
+python-versions = ">=3.9"
+groups = ["dev"]
+files = [
+ {file = "tokenize_rt-6.2.0-py2.py3-none-any.whl", hash = "sha256:a152bf4f249c847a66497a4a95f63376ed68ac6abf092a2f7cfb29d044ecff44"},
+ {file = "tokenize_rt-6.2.0.tar.gz", hash = "sha256:8439c042b330c553fdbe1758e4a05c0ed460dbbbb24a606f11f0dee75da4cad6"},
+]
+
+[[package]]
+name = "tokenizers"
+version = "0.22.0"
+description = ""
+optional = false
+python-versions = ">=3.9"
+groups = ["main"]
+files = [
+ {file = "tokenizers-0.22.0-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:eaa9620122a3fb99b943f864af95ed14c8dfc0f47afa3b404ac8c16b3f2bb484"},
+ {file = "tokenizers-0.22.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:71784b9ab5bf0ff3075bceeb198149d2c5e068549c0d18fe32d06ba0deb63f79"},
+ {file = "tokenizers-0.22.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec5b71f668a8076802b0241a42387d48289f25435b86b769ae1837cad4172a17"},
+ {file = "tokenizers-0.22.0-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ea8562fa7498850d02a16178105b58803ea825b50dc9094d60549a7ed63654bb"},
+ {file = "tokenizers-0.22.0-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4136e1558a9ef2e2f1de1555dcd573e1cbc4a320c1a06c4107a3d46dc8ac6e4b"},
+ {file = "tokenizers-0.22.0-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cdf5954de3962a5fd9781dc12048d24a1a6f1f5df038c6e95db328cd22964206"},
+ {file = "tokenizers-0.22.0-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8337ca75d0731fc4860e6204cc24bb36a67d9736142aa06ed320943b50b1e7ed"},
+ {file = "tokenizers-0.22.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a89264e26f63c449d8cded9061adea7b5de53ba2346fc7e87311f7e4117c1cc8"},
+ {file = "tokenizers-0.22.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:790bad50a1b59d4c21592f9c3cf5e5cf9c3c7ce7e1a23a739f13e01fb1be377a"},
+ {file = "tokenizers-0.22.0-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:76cf6757c73a10ef10bf06fa937c0ec7393d90432f543f49adc8cab3fb6f26cb"},
+ {file = "tokenizers-0.22.0-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:1626cb186e143720c62c6c6b5371e62bbc10af60481388c0da89bc903f37ea0c"},
+ {file = "tokenizers-0.22.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:da589a61cbfea18ae267723d6b029b84598dc8ca78db9951d8f5beff72d8507c"},
+ {file = "tokenizers-0.22.0-cp39-abi3-win32.whl", hash = "sha256:dbf9d6851bddae3e046fedfb166f47743c1c7bd11c640f0691dd35ef0bcad3be"},
+ {file = "tokenizers-0.22.0-cp39-abi3-win_amd64.whl", hash = "sha256:c78174859eeaee96021f248a56c801e36bfb6bd5b067f2e95aa82445ca324f00"},
+ {file = "tokenizers-0.22.0.tar.gz", hash = "sha256:2e33b98525be8453f355927f3cab312c36cd3e44f4d7e9e97da2fa94d0a49dcb"},
+]
+
+[package.dependencies]
+huggingface-hub = ">=0.16.4,<1.0"
+
+[package.extras]
+dev = ["tokenizers[testing]"]
+docs = ["setuptools-rust", "sphinx", "sphinx-rtd-theme"]
+testing = ["black (==22.3)", "datasets", "numpy", "pytest", "pytest-asyncio", "requests", "ruff"]
+
+[[package]]
+name = "tomli"
+version = "2.2.1"
+description = "A lil' TOML parser"
+optional = false
+python-versions = ">=3.8"
+groups = ["dev", "docs"]
+markers = "python_version < \"3.11\""
+files = [
+ {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"},
+ {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"},
+ {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a"},
+ {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee"},
+ {file = "tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e"},
+ {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4"},
+ {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106"},
+ {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8"},
+ {file = "tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff"},
+ {file = "tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b"},
+ {file = "tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea"},
+ {file = "tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8"},
+ {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192"},
+ {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222"},
+ {file = "tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77"},
+ {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6"},
+ {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd"},
+ {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e"},
+ {file = "tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98"},
+ {file = "tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4"},
+ {file = "tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7"},
+ {file = "tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c"},
+ {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13"},
+ {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281"},
+ {file = "tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272"},
+ {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140"},
+ {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2"},
+ {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744"},
+ {file = "tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec"},
+ {file = "tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69"},
+ {file = "tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc"},
+ {file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"},
+]
+
+[[package]]
+name = "tomlkit"
+version = "0.13.3"
+description = "Style preserving TOML library"
+optional = false
+python-versions = ">=3.8"
+groups = ["dev"]
+files = [
+ {file = "tomlkit-0.13.3-py3-none-any.whl", hash = "sha256:c89c649d79ee40629a9fda55f8ace8c6a1b42deb912b2a8fd8d942ddadb606b0"},
+ {file = "tomlkit-0.13.3.tar.gz", hash = "sha256:430cf247ee57df2b94ee3fbe588e71d362a941ebb545dec29b53961d61add2a1"},
+]
+
+[[package]]
+name = "torch"
+version = "2.7.1"
+description = "Tensors and Dynamic neural networks in Python with strong GPU acceleration"
+optional = false
+python-versions = ">=3.9.0"
+groups = ["main"]
+markers = "python_version >= \"3.12\""
+files = [
+ {file = "torch-2.7.1-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:a103b5d782af5bd119b81dbcc7ffc6fa09904c423ff8db397a1e6ea8fd71508f"},
+ {file = "torch-2.7.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:fe955951bdf32d182ee8ead6c3186ad54781492bf03d547d31771a01b3d6fb7d"},
+ {file = "torch-2.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:885453d6fba67d9991132143bf7fa06b79b24352f4506fd4d10b309f53454162"},
+ {file = "torch-2.7.1-cp310-none-macosx_11_0_arm64.whl", hash = "sha256:d72acfdb86cee2a32c0ce0101606f3758f0d8bb5f8f31e7920dc2809e963aa7c"},
+ {file = "torch-2.7.1-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:236f501f2e383f1cb861337bdf057712182f910f10aeaf509065d54d339e49b2"},
+ {file = "torch-2.7.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:06eea61f859436622e78dd0cdd51dbc8f8c6d76917a9cf0555a333f9eac31ec1"},
+ {file = "torch-2.7.1-cp311-cp311-win_amd64.whl", hash = "sha256:8273145a2e0a3c6f9fd2ac36762d6ee89c26d430e612b95a99885df083b04e52"},
+ {file = "torch-2.7.1-cp311-none-macosx_11_0_arm64.whl", hash = "sha256:aea4fc1bf433d12843eb2c6b2204861f43d8364597697074c8d38ae2507f8730"},
+ {file = "torch-2.7.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:27ea1e518df4c9de73af7e8a720770f3628e7f667280bce2be7a16292697e3fa"},
+ {file = "torch-2.7.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:c33360cfc2edd976c2633b3b66c769bdcbbf0e0b6550606d188431c81e7dd1fc"},
+ {file = "torch-2.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:d8bf6e1856ddd1807e79dc57e54d3335f2b62e6f316ed13ed3ecfe1fc1df3d8b"},
+ {file = "torch-2.7.1-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:787687087412c4bd68d315e39bc1223f08aae1d16a9e9771d95eabbb04ae98fb"},
+ {file = "torch-2.7.1-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:03563603d931e70722dce0e11999d53aa80a375a3d78e6b39b9f6805ea0a8d28"},
+ {file = "torch-2.7.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:d632f5417b6980f61404a125b999ca6ebd0b8b4bbdbb5fbbba44374ab619a412"},
+ {file = "torch-2.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:23660443e13995ee93e3d844786701ea4ca69f337027b05182f5ba053ce43b38"},
+ {file = "torch-2.7.1-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:0da4f4dba9f65d0d203794e619fe7ca3247a55ffdcbd17ae8fb83c8b2dc9b585"},
+ {file = "torch-2.7.1-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:e08d7e6f21a617fe38eeb46dd2213ded43f27c072e9165dc27300c9ef9570934"},
+ {file = "torch-2.7.1-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:30207f672328a42df4f2174b8f426f354b2baa0b7cca3a0adb3d6ab5daf00dc8"},
+ {file = "torch-2.7.1-cp313-cp313t-win_amd64.whl", hash = "sha256:79042feca1c634aaf6603fe6feea8c6b30dfa140a6bbc0b973e2260c7e79a22e"},
+ {file = "torch-2.7.1-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:988b0cbc4333618a1056d2ebad9eb10089637b659eb645434d0809d8d937b946"},
+ {file = "torch-2.7.1-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:e0d81e9a12764b6f3879a866607c8ae93113cbcad57ce01ebde63eb48a576369"},
+ {file = "torch-2.7.1-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:8394833c44484547ed4a47162318337b88c97acdb3273d85ea06e03ffff44998"},
+ {file = "torch-2.7.1-cp39-cp39-win_amd64.whl", hash = "sha256:df41989d9300e6e3c19ec9f56f856187a6ef060c3662fe54f4b6baf1fc90bd19"},
+ {file = "torch-2.7.1-cp39-none-macosx_11_0_arm64.whl", hash = "sha256:a737b5edd1c44a5c1ece2e9f3d00df9d1b3fb9541138bee56d83d38293fb6c9d"},
+]
+
+[package.dependencies]
+filelock = "*"
+fsspec = "*"
+jinja2 = "*"
+networkx = "*"
+nvidia-cublas-cu12 = {version = "12.6.4.1", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""}
+nvidia-cuda-cupti-cu12 = {version = "12.6.80", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""}
+nvidia-cuda-nvrtc-cu12 = {version = "12.6.77", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""}
+nvidia-cuda-runtime-cu12 = {version = "12.6.77", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""}
+nvidia-cudnn-cu12 = {version = "9.5.1.17", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""}
+nvidia-cufft-cu12 = {version = "11.3.0.4", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""}
+nvidia-cufile-cu12 = {version = "1.11.1.6", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""}
+nvidia-curand-cu12 = {version = "10.3.7.77", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""}
+nvidia-cusolver-cu12 = {version = "11.7.1.2", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""}
+nvidia-cusparse-cu12 = {version = "12.5.4.2", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""}
+nvidia-cusparselt-cu12 = {version = "0.6.3", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""}
+nvidia-nccl-cu12 = {version = "2.26.2", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""}
+nvidia-nvjitlink-cu12 = {version = "12.6.85", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""}
+nvidia-nvtx-cu12 = {version = "12.6.77", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""}
+setuptools = {version = "*", markers = "python_version >= \"3.12\""}
+sympy = ">=1.13.3"
+triton = {version = "3.3.1", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""}
+typing-extensions = ">=4.10.0"
+
+[package.extras]
+opt-einsum = ["opt-einsum (>=3.3)"]
+optree = ["optree (>=0.13.0)"]
+
+[[package]]
+name = "torch"
+version = "2.8.0"
+description = "Tensors and Dynamic neural networks in Python with strong GPU acceleration"
+optional = false
+python-versions = ">=3.9.0"
+groups = ["main"]
+markers = "python_version <= \"3.11\""
+files = [
+ {file = "torch-2.8.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:0be92c08b44009d4131d1ff7a8060d10bafdb7ddcb7359ef8d8c5169007ea905"},
+ {file = "torch-2.8.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:89aa9ee820bb39d4d72b794345cccef106b574508dd17dbec457949678c76011"},
+ {file = "torch-2.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:e8e5bf982e87e2b59d932769938b698858c64cc53753894be25629bdf5cf2f46"},
+ {file = "torch-2.8.0-cp310-none-macosx_11_0_arm64.whl", hash = "sha256:a3f16a58a9a800f589b26d47ee15aca3acf065546137fc2af039876135f4c760"},
+ {file = "torch-2.8.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:220a06fd7af8b653c35d359dfe1aaf32f65aa85befa342629f716acb134b9710"},
+ {file = "torch-2.8.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:c12fa219f51a933d5f80eeb3a7a5d0cbe9168c0a14bbb4055f1979431660879b"},
+ {file = "torch-2.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:8c7ef765e27551b2fbfc0f41bcf270e1292d9bf79f8e0724848b1682be6e80aa"},
+ {file = "torch-2.8.0-cp311-none-macosx_11_0_arm64.whl", hash = "sha256:5ae0524688fb6707c57a530c2325e13bb0090b745ba7b4a2cd6a3ce262572916"},
+ {file = "torch-2.8.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:e2fab4153768d433f8ed9279c8133a114a034a61e77a3a104dcdf54388838705"},
+ {file = "torch-2.8.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:b2aca0939fb7e4d842561febbd4ffda67a8e958ff725c1c27e244e85e982173c"},
+ {file = "torch-2.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:2f4ac52f0130275d7517b03a33d2493bab3693c83dcfadf4f81688ea82147d2e"},
+ {file = "torch-2.8.0-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:619c2869db3ada2c0105487ba21b5008defcc472d23f8b80ed91ac4a380283b0"},
+ {file = "torch-2.8.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:2b2f96814e0345f5a5aed9bf9734efa913678ed19caf6dc2cddb7930672d6128"},
+ {file = "torch-2.8.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:65616ca8ec6f43245e1f5f296603e33923f4c30f93d65e103d9e50c25b35150b"},
+ {file = "torch-2.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:659df54119ae03e83a800addc125856effda88b016dfc54d9f65215c3975be16"},
+ {file = "torch-2.8.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:1a62a1ec4b0498930e2543535cf70b1bef8c777713de7ceb84cd79115f553767"},
+ {file = "torch-2.8.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:83c13411a26fac3d101fe8035a6b0476ae606deb8688e904e796a3534c197def"},
+ {file = "torch-2.8.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:8f0a9d617a66509ded240add3754e462430a6c1fc5589f86c17b433dd808f97a"},
+ {file = "torch-2.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:a7242b86f42be98ac674b88a4988643b9bc6145437ec8f048fea23f72feb5eca"},
+ {file = "torch-2.8.0-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:7b677e17f5a3e69fdef7eb3b9da72622f8d322692930297e4ccb52fefc6c8211"},
+ {file = "torch-2.8.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:da6afa31c13b669d4ba49d8a2169f0db2c3ec6bec4af898aa714f401d4c38904"},
+ {file = "torch-2.8.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:06fcee8000e5c62a9f3e52a688b9c5abb7c6228d0e56e3452983416025c41381"},
+ {file = "torch-2.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:5128fe752a355d9308e56af1ad28b15266fe2da5948660fad44de9e3a9e36e8c"},
+ {file = "torch-2.8.0-cp39-none-macosx_11_0_arm64.whl", hash = "sha256:e9f071f5b52a9f6970dc8a919694b27a91ae9dc08898b2b988abbef5eddfd1ae"},
+]
+
+[package.dependencies]
+filelock = "*"
+fsspec = "*"
+jinja2 = "*"
+networkx = "*"
+nvidia-cublas-cu12 = {version = "12.8.4.1", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""}
+nvidia-cuda-cupti-cu12 = {version = "12.8.90", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""}
+nvidia-cuda-nvrtc-cu12 = {version = "12.8.93", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""}
+nvidia-cuda-runtime-cu12 = {version = "12.8.90", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""}
+nvidia-cudnn-cu12 = {version = "9.10.2.21", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""}
+nvidia-cufft-cu12 = {version = "11.3.3.83", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""}
+nvidia-cufile-cu12 = {version = "1.13.1.3", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""}
+nvidia-curand-cu12 = {version = "10.3.9.90", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""}
+nvidia-cusolver-cu12 = {version = "11.7.3.90", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""}
+nvidia-cusparse-cu12 = {version = "12.5.8.93", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""}
+nvidia-cusparselt-cu12 = {version = "0.7.1", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""}
+nvidia-nccl-cu12 = {version = "2.27.3", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""}
+nvidia-nvjitlink-cu12 = {version = "12.8.93", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""}
+nvidia-nvtx-cu12 = {version = "12.8.90", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""}
+sympy = ">=1.13.3"
+triton = {version = "3.4.0", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""}
+typing-extensions = ">=4.10.0"
+
+[package.extras]
+opt-einsum = ["opt-einsum (>=3.3)"]
+optree = ["optree (>=0.13.0)"]
+pyyaml = ["pyyaml"]
+
+[[package]]
+name = "tqdm"
+version = "4.67.1"
+description = "Fast, Extensible Progress Meter"
+optional = false
+python-versions = ">=3.7"
+groups = ["main", "benchmarks"]
+files = [
+ {file = "tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2"},
+ {file = "tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2"},
+]
+
+[package.dependencies]
+colorama = {version = "*", markers = "platform_system == \"Windows\""}
+
+[package.extras]
+dev = ["nbval", "pytest (>=6)", "pytest-asyncio (>=0.24)", "pytest-cov", "pytest-timeout"]
+discord = ["requests"]
+notebook = ["ipywidgets (>=6)"]
+slack = ["slack-sdk"]
+telegram = ["requests"]
+
+[[package]]
+name = "transformers"
+version = "4.56.0"
+description = "State-of-the-art Machine Learning for JAX, PyTorch and TensorFlow"
+optional = false
+python-versions = ">=3.9.0"
+groups = ["main"]
+files = [
+ {file = "transformers-4.56.0-py3-none-any.whl", hash = "sha256:bacf539c38dd850690856881c4974321af93a22f2ee96bcc994741a2121d8e71"},
+ {file = "transformers-4.56.0.tar.gz", hash = "sha256:6ca9c3f38aa4da93ebf877db7156368c1c188c7465f09dbe70951e7622e987fa"},
+]
+
+[package.dependencies]
+filelock = "*"
+huggingface-hub = ">=0.34.0,<1.0"
+numpy = ">=1.17"
+packaging = ">=20.0"
+pyyaml = ">=5.1"
+regex = "!=2019.12.17"
+requests = "*"
+safetensors = ">=0.4.3"
+tokenizers = ">=0.22.0,<=0.23.0"
+tqdm = ">=4.27"
+
+[package.extras]
+accelerate = ["accelerate (>=0.26.0)"]
+all = ["Pillow (>=10.0.1,<=15.0)", "accelerate (>=0.26.0)", "av", "codecarbon (>=2.8.1)", "flax (>=0.4.1,<=0.7.0)", "jax (>=0.4.1,<=0.4.13)", "jaxlib (>=0.4.1,<=0.4.13)", "jinja2 (>=3.1.0)", "kenlm", "keras-nlp (>=0.3.1,<0.14.0)", "kernels (>=0.6.1,<=0.9)", "librosa", "mistral-common[opencv] (>=1.6.3)", "num2words", "onnxconverter-common", "optax (>=0.0.8,<=0.1.4)", "optuna", "phonemizer", "protobuf", "pyctcdecode (>=0.4.0)", "ray[tune] (>=2.7.0)", "scipy (<1.13.0)", "sentencepiece (>=0.1.91,!=0.1.92)", "sigopt", "tensorflow (>2.9,<2.16)", "tensorflow-text (<2.16)", "tf2onnx", "timm (!=1.0.18,<=1.0.19)", "tokenizers (>=0.22.0,<=0.23.0)", "torch (>=2.2)", "torchaudio", "torchvision"]
+audio = ["kenlm", "librosa", "phonemizer", "pyctcdecode (>=0.4.0)"]
+benchmark = ["optimum-benchmark (>=0.3.0)"]
+chat-template = ["jinja2 (>=3.1.0)"]
+codecarbon = ["codecarbon (>=2.8.1)"]
+deepspeed = ["accelerate (>=0.26.0)", "deepspeed (>=0.9.3)"]
+deepspeed-testing = ["GitPython (<3.1.19)", "accelerate (>=0.26.0)", "beautifulsoup4", "cookiecutter (==1.7.3)", "datasets (>=2.15.0)", "deepspeed (>=0.9.3)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "libcst", "mistral-common[opencv] (>=1.6.3)", "nltk (<=3.8.1)", "optuna", "parameterized (>=0.9)", "protobuf", "psutil", "pydantic (>=2)", "pytest (>=7.2.0)", "pytest-asyncio", "pytest-order", "pytest-rerunfailures", "pytest-rich", "pytest-timeout", "pytest-xdist", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (==0.11.2)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "sentencepiece (>=0.1.91,!=0.1.92)", "tensorboard", "timeout-decorator"]
+dev = ["GitPython (<3.1.19)", "Pillow (>=10.0.1,<=15.0)", "accelerate (>=0.26.0)", "av", "beautifulsoup4", "codecarbon (>=2.8.1)", "cookiecutter (==1.7.3)", "datasets (>=2.15.0)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "flax (>=0.4.1,<=0.7.0)", "fugashi (>=1.0)", "ipadic (>=1.0.0,<2.0)", "jax (>=0.4.1,<=0.4.13)", "jaxlib (>=0.4.1,<=0.4.13)", "jinja2 (>=3.1.0)", "kenlm", "keras-nlp (>=0.3.1,<0.14.0)", "kernels (>=0.6.1,<=0.9)", "libcst", "librosa", "mistral-common[opencv] (>=1.6.3)", "nltk (<=3.8.1)", "num2words", "onnxconverter-common", "optax (>=0.0.8,<=0.1.4)", "optuna", "pandas (<2.3.0)", "parameterized (>=0.9)", "phonemizer", "protobuf", "psutil", "pyctcdecode (>=0.4.0)", "pydantic (>=2)", "pytest (>=7.2.0)", "pytest-asyncio", "pytest-order", "pytest-rerunfailures", "pytest-rich", "pytest-timeout", "pytest-xdist", "ray[tune] (>=2.7.0)", "rhoknp (>=1.1.0,<1.3.1)", "rich", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (==0.11.2)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "scikit-learn", "scipy (<1.13.0)", "sentencepiece (>=0.1.91,!=0.1.92)", "sigopt", "sudachidict-core (>=20220729)", "sudachipy (>=0.6.6)", "tensorboard", "tensorflow (>2.9,<2.16)", "tensorflow-text (<2.16)", "tf2onnx", "timeout-decorator", "timm (!=1.0.18,<=1.0.19)", "tokenizers (>=0.22.0,<=0.23.0)", "torch (>=2.2)", "torchaudio", "torchvision", "unidic (>=1.0.2)", "unidic-lite (>=1.0.7)", "urllib3 (<2.0.0)"]
+dev-tensorflow = ["GitPython (<3.1.19)", "Pillow (>=10.0.1,<=15.0)", "beautifulsoup4", "cookiecutter (==1.7.3)", "datasets (>=2.15.0)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "kenlm", "keras-nlp (>=0.3.1,<0.14.0)", "libcst", "librosa", "mistral-common[opencv] (>=1.6.3)", "nltk (<=3.8.1)", "onnxconverter-common", "onnxruntime (>=1.4.0)", "onnxruntime-tools (>=1.4.2)", "pandas (<2.3.0)", "parameterized (>=0.9)", "phonemizer", "protobuf", "psutil", "pyctcdecode (>=0.4.0)", "pydantic (>=2)", "pytest (>=7.2.0)", "pytest-asyncio", "pytest-order", "pytest-rerunfailures", "pytest-rich", "pytest-timeout", "pytest-xdist", "rich", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (==0.11.2)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "scikit-learn", "sentencepiece (>=0.1.91,!=0.1.92)", "tensorboard", "tensorflow (>2.9,<2.16)", "tensorflow-text (<2.16)", "tf2onnx", "timeout-decorator", "tokenizers (>=0.22.0,<=0.23.0)", "urllib3 (<2.0.0)"]
+dev-torch = ["GitPython (<3.1.19)", "Pillow (>=10.0.1,<=15.0)", "accelerate (>=0.26.0)", "beautifulsoup4", "codecarbon (>=2.8.1)", "cookiecutter (==1.7.3)", "datasets (>=2.15.0)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "fugashi (>=1.0)", "ipadic (>=1.0.0,<2.0)", "kenlm", "kernels (>=0.6.1,<=0.9)", "libcst", "librosa", "mistral-common[opencv] (>=1.6.3)", "nltk (<=3.8.1)", "num2words", "onnxruntime (>=1.4.0)", "onnxruntime-tools (>=1.4.2)", "optuna", "pandas (<2.3.0)", "parameterized (>=0.9)", "phonemizer", "protobuf", "psutil", "pyctcdecode (>=0.4.0)", "pydantic (>=2)", "pytest (>=7.2.0)", "pytest-asyncio", "pytest-order", "pytest-rerunfailures", "pytest-rich", "pytest-timeout", "pytest-xdist", "ray[tune] (>=2.7.0)", "rhoknp (>=1.1.0,<1.3.1)", "rich", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (==0.11.2)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "scikit-learn", "sentencepiece (>=0.1.91,!=0.1.92)", "sigopt", "sudachidict-core (>=20220729)", "sudachipy (>=0.6.6)", "tensorboard", "timeout-decorator", "timm (!=1.0.18,<=1.0.19)", "tokenizers (>=0.22.0,<=0.23.0)", "torch (>=2.2)", "torchaudio", "torchvision", "unidic (>=1.0.2)", "unidic-lite (>=1.0.7)", "urllib3 (<2.0.0)"]
+flax = ["flax (>=0.4.1,<=0.7.0)", "jax (>=0.4.1,<=0.4.13)", "jaxlib (>=0.4.1,<=0.4.13)", "optax (>=0.0.8,<=0.1.4)", "scipy (<1.13.0)"]
+flax-speech = ["kenlm", "librosa", "phonemizer", "pyctcdecode (>=0.4.0)"]
+ftfy = ["ftfy"]
+hf-xet = ["hf-xet"]
+hub-kernels = ["kernels (>=0.6.1,<=0.9)"]
+integrations = ["kernels (>=0.6.1,<=0.9)", "optuna", "ray[tune] (>=2.7.0)", "sigopt"]
+ja = ["fugashi (>=1.0)", "ipadic (>=1.0.0,<2.0)", "rhoknp (>=1.1.0,<1.3.1)", "sudachidict-core (>=20220729)", "sudachipy (>=0.6.6)", "unidic (>=1.0.2)", "unidic-lite (>=1.0.7)"]
+mistral-common = ["mistral-common[opencv] (>=1.6.3)"]
+modelcreation = ["cookiecutter (==1.7.3)"]
+natten = ["natten (>=0.14.6,<0.15.0)"]
+num2words = ["num2words"]
+onnx = ["onnxconverter-common", "onnxruntime (>=1.4.0)", "onnxruntime-tools (>=1.4.2)", "tf2onnx"]
+onnxruntime = ["onnxruntime (>=1.4.0)", "onnxruntime-tools (>=1.4.2)"]
+open-telemetry = ["opentelemetry-api", "opentelemetry-exporter-otlp", "opentelemetry-sdk"]
+optuna = ["optuna"]
+quality = ["GitPython (<3.1.19)", "datasets (>=2.15.0)", "libcst", "pandas (<2.3.0)", "rich", "ruff (==0.11.2)", "urllib3 (<2.0.0)"]
+ray = ["ray[tune] (>=2.7.0)"]
+retrieval = ["datasets (>=2.15.0)", "faiss-cpu"]
+ruff = ["ruff (==0.11.2)"]
+sagemaker = ["sagemaker (>=2.31.0)"]
+sentencepiece = ["protobuf", "sentencepiece (>=0.1.91,!=0.1.92)"]
+serving = ["accelerate (>=0.26.0)", "fastapi", "openai (>=1.98.0)", "pydantic (>=2)", "starlette", "torch (>=2.2)", "uvicorn"]
+sigopt = ["sigopt"]
+sklearn = ["scikit-learn"]
+speech = ["kenlm", "librosa", "phonemizer", "pyctcdecode (>=0.4.0)", "torchaudio"]
+testing = ["GitPython (<3.1.19)", "beautifulsoup4", "cookiecutter (==1.7.3)", "datasets (>=2.15.0)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "libcst", "mistral-common[opencv] (>=1.6.3)", "nltk (<=3.8.1)", "parameterized (>=0.9)", "psutil", "pydantic (>=2)", "pytest (>=7.2.0)", "pytest-asyncio", "pytest-order", "pytest-rerunfailures", "pytest-rich", "pytest-timeout", "pytest-xdist", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (==0.11.2)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "sentencepiece (>=0.1.91,!=0.1.92)", "tensorboard", "timeout-decorator"]
+tf = ["keras-nlp (>=0.3.1,<0.14.0)", "onnxconverter-common", "tensorflow (>2.9,<2.16)", "tensorflow-text (<2.16)", "tf2onnx"]
+tf-cpu = ["keras (>2.9,<2.16)", "keras-nlp (>=0.3.1,<0.14.0)", "onnxconverter-common", "tensorflow-cpu (>2.9,<2.16)", "tensorflow-probability (<0.24)", "tensorflow-text (<2.16)", "tf2onnx"]
+tf-speech = ["kenlm", "librosa", "phonemizer", "pyctcdecode (>=0.4.0)"]
+tiktoken = ["blobfile", "tiktoken"]
+timm = ["timm (!=1.0.18,<=1.0.19)"]
+tokenizers = ["tokenizers (>=0.22.0,<=0.23.0)"]
+torch = ["accelerate (>=0.26.0)", "torch (>=2.2)"]
+torch-speech = ["kenlm", "librosa", "phonemizer", "pyctcdecode (>=0.4.0)", "torchaudio"]
+torch-vision = ["Pillow (>=10.0.1,<=15.0)", "torchvision"]
+torchhub = ["filelock", "huggingface-hub (>=0.34.0,<1.0)", "importlib-metadata", "numpy (>=1.17)", "packaging (>=20.0)", "protobuf", "regex (!=2019.12.17)", "requests", "sentencepiece (>=0.1.91,!=0.1.92)", "tokenizers (>=0.22.0,<=0.23.0)", "torch (>=2.2)", "tqdm (>=4.27)"]
+video = ["av"]
+vision = ["Pillow (>=10.0.1,<=15.0)"]
+
+[[package]]
+name = "triton"
+version = "3.3.1"
+description = "A language and compiler for custom Deep Learning operations"
+optional = false
+python-versions = "*"
+groups = ["main"]
+markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version >= \"3.12\""
+files = [
+ {file = "triton-3.3.1-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b74db445b1c562844d3cfad6e9679c72e93fdfb1a90a24052b03bb5c49d1242e"},
+ {file = "triton-3.3.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b31e3aa26f8cb3cc5bf4e187bf737cbacf17311e1112b781d4a059353dfd731b"},
+ {file = "triton-3.3.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9999e83aba21e1a78c1f36f21bce621b77bcaa530277a50484a7cb4a822f6e43"},
+ {file = "triton-3.3.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b89d846b5a4198317fec27a5d3a609ea96b6d557ff44b56c23176546023c4240"},
+ {file = "triton-3.3.1-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a3198adb9d78b77818a5388bff89fa72ff36f9da0bc689db2f0a651a67ce6a42"},
+ {file = "triton-3.3.1-cp39-cp39-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f6139aeb04a146b0b8e0fbbd89ad1e65861c57cfed881f21d62d3cb94a36bab7"},
+]
+
+[package.dependencies]
+setuptools = ">=40.8.0"
+
+[package.extras]
+build = ["cmake (>=3.20)", "lit"]
+tests = ["autopep8", "isort", "llnl-hatchet", "numpy", "pytest", "pytest-forked", "pytest-xdist", "scipy (>=1.7.1)"]
+tutorials = ["matplotlib", "pandas", "tabulate"]
+
+[[package]]
+name = "triton"
+version = "3.4.0"
+description = "A language and compiler for custom Deep Learning operations"
+optional = false
+python-versions = "<3.14,>=3.9"
+groups = ["main"]
+markers = "platform_machine == \"x86_64\" and platform_system == \"Linux\" and python_version <= \"3.11\""
+files = [
+ {file = "triton-3.4.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7ff2785de9bc02f500e085420273bb5cc9c9bb767584a4aa28d6e360cec70128"},
+ {file = "triton-3.4.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7b70f5e6a41e52e48cfc087436c8a28c17ff98db369447bcaff3b887a3ab4467"},
+ {file = "triton-3.4.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:31c1d84a5c0ec2c0f8e8a072d7fd150cab84a9c239eaddc6706c081bfae4eb04"},
+ {file = "triton-3.4.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00be2964616f4c619193cb0d1b29a99bd4b001d7dc333816073f92cf2a8ccdeb"},
+ {file = "triton-3.4.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7936b18a3499ed62059414d7df563e6c163c5e16c3773678a3ee3d417865035d"},
+ {file = "triton-3.4.0-cp39-cp39-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:98e5c1442eaeabae2e2452ae765801bd53cd4ce873cab0d1bdd59a32ab2d9397"},
+]
+
+[package.dependencies]
+importlib-metadata = {version = "*", markers = "python_version < \"3.10\""}
+setuptools = ">=40.8.0"
+
+[package.extras]
+build = ["cmake (>=3.20,<4.0)", "lit"]
+tests = ["autopep8", "isort", "llnl-hatchet", "numpy", "pytest", "pytest-forked", "pytest-xdist", "scipy (>=1.7.1)"]
+tutorials = ["matplotlib", "pandas", "tabulate"]
+
+[[package]]
+name = "types-pyyaml"
+version = "6.0.12.20250822"
+description = "Typing stubs for PyYAML"
+optional = false
+python-versions = ">=3.9"
+groups = ["dev"]
+files = [
+ {file = "types_pyyaml-6.0.12.20250822-py3-none-any.whl", hash = "sha256:1fe1a5e146aa315483592d292b72a172b65b946a6d98aa6ddd8e4aa838ab7098"},
+ {file = "types_pyyaml-6.0.12.20250822.tar.gz", hash = "sha256:259f1d93079d335730a9db7cff2bcaf65d7e04b4a56b5927d49a612199b59413"},
+]
+
+[[package]]
+name = "types-requests"
+version = "2.32.4.20250809"
+description = "Typing stubs for requests"
+optional = false
+python-versions = ">=3.9"
+groups = ["dev"]
+files = [
+ {file = "types_requests-2.32.4.20250809-py3-none-any.whl", hash = "sha256:f73d1832fb519ece02c85b1f09d5f0dd3108938e7d47e7f94bbfa18a6782b163"},
+ {file = "types_requests-2.32.4.20250809.tar.gz", hash = "sha256:d8060de1c8ee599311f56ff58010fb4902f462a1470802cf9f6ed27bc46c4df3"},
+]
+
+[package.dependencies]
+urllib3 = ">=2"
+
+[[package]]
+name = "types-setuptools"
+version = "80.9.0.20250822"
+description = "Typing stubs for setuptools"
+optional = false
+python-versions = ">=3.9"
+groups = ["dev"]
+files = [
+ {file = "types_setuptools-80.9.0.20250822-py3-none-any.whl", hash = "sha256:53bf881cb9d7e46ed12c76ef76c0aaf28cfe6211d3fab12e0b83620b1a8642c3"},
+ {file = "types_setuptools-80.9.0.20250822.tar.gz", hash = "sha256:070ea7716968ec67a84c7f7768d9952ff24d28b65b6594797a464f1b3066f965"},
+]
+
+[[package]]
+name = "typing-extensions"
+version = "4.15.0"
+description = "Backported and Experimental Type Hints for Python 3.9+"
+optional = false
+python-versions = ">=3.9"
+groups = ["main", "benchmarks", "dev"]
+files = [
+ {file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"},
+ {file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"},
+]
+
+[[package]]
+name = "typing-inspection"
+version = "0.4.1"
+description = "Runtime typing introspection tools"
+optional = false
+python-versions = ">=3.9"
+groups = ["benchmarks"]
+files = [
+ {file = "typing_inspection-0.4.1-py3-none-any.whl", hash = "sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51"},
+ {file = "typing_inspection-0.4.1.tar.gz", hash = "sha256:6ae134cc0203c33377d43188d4064e9b357dba58cff3185f22924610e70a9d28"},
+]
+
+[package.dependencies]
+typing-extensions = ">=4.12.0"
+
+[[package]]
+name = "tzdata"
+version = "2025.2"
+description = "Provider of IANA time zone data"
+optional = false
+python-versions = ">=2"
+groups = ["main"]
+files = [
+ {file = "tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8"},
+ {file = "tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9"},
+]
+
+[[package]]
+name = "untokenize"
+version = "0.1.1"
+description = "Transforms tokens into original source code (while preserving whitespace)."
+optional = false
+python-versions = "*"
+groups = ["dev"]
+files = [
+ {file = "untokenize-0.1.1.tar.gz", hash = "sha256:3865dbbbb8efb4bb5eaa72f1be7f3e0be00ea8b7f125c69cbd1f5fda926f37a2"},
+]
+
+[[package]]
+name = "urllib3"
+version = "2.5.0"
+description = "HTTP library with thread-safe connection pooling, file post, and more."
+optional = false
+python-versions = ">=3.9"
+groups = ["main", "benchmarks", "dev", "docs"]
+files = [
+ {file = "urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc"},
+ {file = "urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760"},
+]
+
+[package.extras]
+brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""]
+h2 = ["h2 (>=4,<5)"]
+socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"]
+zstd = ["zstandard (>=0.18.0)"]
+
+[[package]]
+name = "virtualenv"
+version = "20.34.0"
+description = "Virtual Python Environment builder"
+optional = false
+python-versions = ">=3.8"
+groups = ["dev"]
+files = [
+ {file = "virtualenv-20.34.0-py3-none-any.whl", hash = "sha256:341f5afa7eee943e4984a9207c025feedd768baff6753cd660c857ceb3e36026"},
+ {file = "virtualenv-20.34.0.tar.gz", hash = "sha256:44815b2c9dee7ed86e387b842a84f20b93f7f417f95886ca1996a72a4138eb1a"},
+]
+
+[package.dependencies]
+distlib = ">=0.3.7,<1"
+filelock = ">=3.12.2,<4"
+platformdirs = ">=3.9.1,<5"
+typing-extensions = {version = ">=4.13.2", markers = "python_version < \"3.11\""}
+
+[package.extras]
+docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"]
+test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8) ; platform_python_implementation == \"PyPy\" or platform_python_implementation == \"GraalVM\" or platform_python_implementation == \"CPython\" and sys_platform == \"win32\" and python_version >= \"3.13\"", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10) ; platform_python_implementation == \"CPython\""]
+
+[[package]]
+name = "wandb"
+version = "0.21.3"
+description = "A CLI and library for interacting with the Weights & Biases API."
+optional = false
+python-versions = ">=3.8"
+groups = ["benchmarks"]
+files = [
+ {file = "wandb-0.21.3-py3-none-macosx_10_14_x86_64.whl", hash = "sha256:f85bac45b4482742ec9ff190af38eb00a877ddeb4875475e7e487dc19300ff03"},
+ {file = "wandb-0.21.3-py3-none-macosx_12_0_arm64.whl", hash = "sha256:8a2b3ba419b91d47edead2755f04cef54f9e3c4496ee0c9854c3cfeff4216dd3"},
+ {file = "wandb-0.21.3-py3-none-macosx_12_0_x86_64.whl", hash = "sha256:35a1972881f3b85755befab004118234593792a9f05e07fd6345780172f4420e"},
+ {file = "wandb-0.21.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d9cf8588cb090a2a41f589037fda72c57c9e23edfbd2ad829e575f1305d942c"},
+ {file = "wandb-0.21.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ff24b6b8e0f9da840b6bd5c7f60b0a5507bd998db40c9c2d476f9a340bec8ed"},
+ {file = "wandb-0.21.3-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:4975dec19e2b343e23ed6e60f7e1290120553719f82e87a22205bede758416ad"},
+ {file = "wandb-0.21.3-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:514a0aad40ecc0bdb757b1dc86e4ac98f61d2d760445b6e1f555291562320f2d"},
+ {file = "wandb-0.21.3-py3-none-win32.whl", hash = "sha256:45aa3d8ad53c6ee06f37490d7a329ed7d0f5ca4dbd5d05bb0c01d5da22f14691"},
+ {file = "wandb-0.21.3-py3-none-win_amd64.whl", hash = "sha256:56d5a5697766f552a9933d8c6a564202194768eb0389bd5f9fe9a99cd4cee41e"},
+ {file = "wandb-0.21.3.tar.gz", hash = "sha256:031e24e2aad0ce735dfdcc74baf2f2c12c106f500ed24798de6ef9b9e63bb432"},
+]
+
+[package.dependencies]
+click = ">=8.0.1"
+eval-type-backport = {version = "*", markers = "python_version < \"3.10\""}
+gitpython = ">=1.0.0,<3.1.29 || >3.1.29"
+packaging = "*"
+platformdirs = "*"
+protobuf = [
+ {version = ">=3.15.0,<4.21.0 || >4.21.0,<5.28.0 || >5.28.0,<7", markers = "python_version == \"3.9\" and sys_platform == \"linux\""},
+ {version = ">=3.19.0,<4.21.0 || >4.21.0,<5.28.0 || >5.28.0,<7", markers = "python_version > \"3.9\" or sys_platform != \"linux\""},
+]
+pydantic = "<3"
+pyyaml = "*"
+requests = ">=2.0.0,<3"
+sentry-sdk = ">=2.0.0"
+typing-extensions = ">=4.8,<5"
+
+[package.extras]
+aws = ["boto3", "botocore (>=1.5.76)"]
+azure = ["azure-identity", "azure-storage-blob"]
+gcp = ["google-cloud-storage"]
+importers = ["filelock", "mlflow", "polars (<=1.2.1)", "rich", "tenacity"]
+kubeflow = ["google-cloud-storage", "kubernetes", "minio", "sh"]
+launch = ["awscli", "azure-containerregistry", "azure-identity", "azure-storage-blob", "boto3", "botocore (>=1.5.76)", "chardet", "google-auth", "google-cloud-aiplatform", "google-cloud-artifact-registry", "google-cloud-compute", "google-cloud-storage", "iso8601", "jsonschema", "kubernetes", "kubernetes-asyncio", "nbconvert", "nbformat", "optuna", "pydantic", "pyyaml (>=6.0.0)", "tomli", "tornado (>=6.5.0) ; python_version >= \"3.9\"", "typing-extensions"]
+media = ["bokeh", "imageio (>=2.28.1)", "moviepy (>=1.0.0)", "numpy", "pillow", "plotly (>=5.18.0)", "rdkit", "soundfile"]
+models = ["cloudpickle"]
+perf = ["orjson"]
+sweeps = ["sweeps (>=0.2.0)"]
+workspaces = ["wandb-workspaces"]
+
+[[package]]
+name = "wrapt"
+version = "1.17.3"
+description = "Module for decorators, wrappers and monkey patching."
+optional = false
+python-versions = ">=3.8"
+groups = ["dev"]
+files = [
+ {file = "wrapt-1.17.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:88bbae4d40d5a46142e70d58bf664a89b6b4befaea7b2ecc14e03cedb8e06c04"},
+ {file = "wrapt-1.17.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6b13af258d6a9ad602d57d889f83b9d5543acd471eee12eb51f5b01f8eb1bc2"},
+ {file = "wrapt-1.17.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd341868a4b6714a5962c1af0bd44f7c404ef78720c7de4892901e540417111c"},
+ {file = "wrapt-1.17.3-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f9b2601381be482f70e5d1051a5965c25fb3625455a2bf520b5a077b22afb775"},
+ {file = "wrapt-1.17.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:343e44b2a8e60e06a7e0d29c1671a0d9951f59174f3709962b5143f60a2a98bd"},
+ {file = "wrapt-1.17.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:33486899acd2d7d3066156b03465b949da3fd41a5da6e394ec49d271baefcf05"},
+ {file = "wrapt-1.17.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e6f40a8aa5a92f150bdb3e1c44b7e98fb7113955b2e5394122fa5532fec4b418"},
+ {file = "wrapt-1.17.3-cp310-cp310-win32.whl", hash = "sha256:a36692b8491d30a8c75f1dfee65bef119d6f39ea84ee04d9f9311f83c5ad9390"},
+ {file = "wrapt-1.17.3-cp310-cp310-win_amd64.whl", hash = "sha256:afd964fd43b10c12213574db492cb8f73b2f0826c8df07a68288f8f19af2ebe6"},
+ {file = "wrapt-1.17.3-cp310-cp310-win_arm64.whl", hash = "sha256:af338aa93554be859173c39c85243970dc6a289fa907402289eeae7543e1ae18"},
+ {file = "wrapt-1.17.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:273a736c4645e63ac582c60a56b0acb529ef07f78e08dc6bfadf6a46b19c0da7"},
+ {file = "wrapt-1.17.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5531d911795e3f935a9c23eb1c8c03c211661a5060aab167065896bbf62a5f85"},
+ {file = "wrapt-1.17.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0610b46293c59a3adbae3dee552b648b984176f8562ee0dba099a56cfbe4df1f"},
+ {file = "wrapt-1.17.3-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b32888aad8b6e68f83a8fdccbf3165f5469702a7544472bdf41f582970ed3311"},
+ {file = "wrapt-1.17.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cccf4f81371f257440c88faed6b74f1053eef90807b77e31ca057b2db74edb1"},
+ {file = "wrapt-1.17.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8a210b158a34164de8bb68b0e7780041a903d7b00c87e906fb69928bf7890d5"},
+ {file = "wrapt-1.17.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:79573c24a46ce11aab457b472efd8d125e5a51da2d1d24387666cd85f54c05b2"},
+ {file = "wrapt-1.17.3-cp311-cp311-win32.whl", hash = "sha256:c31eebe420a9a5d2887b13000b043ff6ca27c452a9a22fa71f35f118e8d4bf89"},
+ {file = "wrapt-1.17.3-cp311-cp311-win_amd64.whl", hash = "sha256:0b1831115c97f0663cb77aa27d381237e73ad4f721391a9bfb2fe8bc25fa6e77"},
+ {file = "wrapt-1.17.3-cp311-cp311-win_arm64.whl", hash = "sha256:5a7b3c1ee8265eb4c8f1b7d29943f195c00673f5ab60c192eba2d4a7eae5f46a"},
+ {file = "wrapt-1.17.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ab232e7fdb44cdfbf55fc3afa31bcdb0d8980b9b95c38b6405df2acb672af0e0"},
+ {file = "wrapt-1.17.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9baa544e6acc91130e926e8c802a17f3b16fbea0fd441b5a60f5cf2cc5c3deba"},
+ {file = "wrapt-1.17.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6b538e31eca1a7ea4605e44f81a48aa24c4632a277431a6ed3f328835901f4fd"},
+ {file = "wrapt-1.17.3-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:042ec3bb8f319c147b1301f2393bc19dba6e176b7da446853406d041c36c7828"},
+ {file = "wrapt-1.17.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3af60380ba0b7b5aeb329bc4e402acd25bd877e98b3727b0135cb5c2efdaefe9"},
+ {file = "wrapt-1.17.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0b02e424deef65c9f7326d8c19220a2c9040c51dc165cddb732f16198c168396"},
+ {file = "wrapt-1.17.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:74afa28374a3c3a11b3b5e5fca0ae03bef8450d6aa3ab3a1e2c30e3a75d023dc"},
+ {file = "wrapt-1.17.3-cp312-cp312-win32.whl", hash = "sha256:4da9f45279fff3543c371d5ababc57a0384f70be244de7759c85a7f989cb4ebe"},
+ {file = "wrapt-1.17.3-cp312-cp312-win_amd64.whl", hash = "sha256:e71d5c6ebac14875668a1e90baf2ea0ef5b7ac7918355850c0908ae82bcb297c"},
+ {file = "wrapt-1.17.3-cp312-cp312-win_arm64.whl", hash = "sha256:604d076c55e2fdd4c1c03d06dc1a31b95130010517b5019db15365ec4a405fc6"},
+ {file = "wrapt-1.17.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a47681378a0439215912ef542c45a783484d4dd82bac412b71e59cf9c0e1cea0"},
+ {file = "wrapt-1.17.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:54a30837587c6ee3cd1a4d1c2ec5d24e77984d44e2f34547e2323ddb4e22eb77"},
+ {file = "wrapt-1.17.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:16ecf15d6af39246fe33e507105d67e4b81d8f8d2c6598ff7e3ca1b8a37213f7"},
+ {file = "wrapt-1.17.3-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6fd1ad24dc235e4ab88cda009e19bf347aabb975e44fd5c2fb22a3f6e4141277"},
+ {file = "wrapt-1.17.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ed61b7c2d49cee3c027372df5809a59d60cf1b6c2f81ee980a091f3afed6a2d"},
+ {file = "wrapt-1.17.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:423ed5420ad5f5529db9ce89eac09c8a2f97da18eb1c870237e84c5a5c2d60aa"},
+ {file = "wrapt-1.17.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e01375f275f010fcbf7f643b4279896d04e571889b8a5b3f848423d91bf07050"},
+ {file = "wrapt-1.17.3-cp313-cp313-win32.whl", hash = "sha256:53e5e39ff71b3fc484df8a522c933ea2b7cdd0d5d15ae82e5b23fde87d44cbd8"},
+ {file = "wrapt-1.17.3-cp313-cp313-win_amd64.whl", hash = "sha256:1f0b2f40cf341ee8cc1a97d51ff50dddb9fcc73241b9143ec74b30fc4f44f6cb"},
+ {file = "wrapt-1.17.3-cp313-cp313-win_arm64.whl", hash = "sha256:7425ac3c54430f5fc5e7b6f41d41e704db073309acfc09305816bc6a0b26bb16"},
+ {file = "wrapt-1.17.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cf30f6e3c077c8e6a9a7809c94551203c8843e74ba0c960f4a98cd80d4665d39"},
+ {file = "wrapt-1.17.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:e228514a06843cae89621384cfe3a80418f3c04aadf8a3b14e46a7be704e4235"},
+ {file = "wrapt-1.17.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:5ea5eb3c0c071862997d6f3e02af1d055f381b1d25b286b9d6644b79db77657c"},
+ {file = "wrapt-1.17.3-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:281262213373b6d5e4bb4353bc36d1ba4084e6d6b5d242863721ef2bf2c2930b"},
+ {file = "wrapt-1.17.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dc4a8d2b25efb6681ecacad42fca8859f88092d8732b170de6a5dddd80a1c8fa"},
+ {file = "wrapt-1.17.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:373342dd05b1d07d752cecbec0c41817231f29f3a89aa8b8843f7b95992ed0c7"},
+ {file = "wrapt-1.17.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d40770d7c0fd5cbed9d84b2c3f2e156431a12c9a37dc6284060fb4bec0b7ffd4"},
+ {file = "wrapt-1.17.3-cp314-cp314-win32.whl", hash = "sha256:fbd3c8319de8e1dc79d346929cd71d523622da527cca14e0c1d257e31c2b8b10"},
+ {file = "wrapt-1.17.3-cp314-cp314-win_amd64.whl", hash = "sha256:e1a4120ae5705f673727d3253de3ed0e016f7cd78dc463db1b31e2463e1f3cf6"},
+ {file = "wrapt-1.17.3-cp314-cp314-win_arm64.whl", hash = "sha256:507553480670cab08a800b9463bdb881b2edeed77dc677b0a5915e6106e91a58"},
+ {file = "wrapt-1.17.3-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:ed7c635ae45cfbc1a7371f708727bf74690daedc49b4dba310590ca0bd28aa8a"},
+ {file = "wrapt-1.17.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:249f88ed15503f6492a71f01442abddd73856a0032ae860de6d75ca62eed8067"},
+ {file = "wrapt-1.17.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5a03a38adec8066d5a37bea22f2ba6bbf39fcdefbe2d91419ab864c3fb515454"},
+ {file = "wrapt-1.17.3-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5d4478d72eb61c36e5b446e375bbc49ed002430d17cdec3cecb36993398e1a9e"},
+ {file = "wrapt-1.17.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:223db574bb38637e8230eb14b185565023ab624474df94d2af18f1cdb625216f"},
+ {file = "wrapt-1.17.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e405adefb53a435f01efa7ccdec012c016b5a1d3f35459990afc39b6be4d5056"},
+ {file = "wrapt-1.17.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:88547535b787a6c9ce4086917b6e1d291aa8ed914fdd3a838b3539dc95c12804"},
+ {file = "wrapt-1.17.3-cp314-cp314t-win32.whl", hash = "sha256:41b1d2bc74c2cac6f9074df52b2efbef2b30bdfe5f40cb78f8ca22963bc62977"},
+ {file = "wrapt-1.17.3-cp314-cp314t-win_amd64.whl", hash = "sha256:73d496de46cd2cdbdbcce4ae4bcdb4afb6a11234a1df9c085249d55166b95116"},
+ {file = "wrapt-1.17.3-cp314-cp314t-win_arm64.whl", hash = "sha256:f38e60678850c42461d4202739f9bf1e3a737c7ad283638251e79cc49effb6b6"},
+ {file = "wrapt-1.17.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:70d86fa5197b8947a2fa70260b48e400bf2ccacdcab97bb7de47e3d1e6312225"},
+ {file = "wrapt-1.17.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:df7d30371a2accfe4013e90445f6388c570f103d61019b6b7c57e0265250072a"},
+ {file = "wrapt-1.17.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:caea3e9c79d5f0d2c6d9ab96111601797ea5da8e6d0723f77eabb0d4068d2b2f"},
+ {file = "wrapt-1.17.3-cp38-cp38-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:758895b01d546812d1f42204bd443b8c433c44d090248bf22689df673ccafe00"},
+ {file = "wrapt-1.17.3-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:02b551d101f31694fc785e58e0720ef7d9a10c4e62c1c9358ce6f63f23e30a56"},
+ {file = "wrapt-1.17.3-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:656873859b3b50eeebe6db8b1455e99d90c26ab058db8e427046dbc35c3140a5"},
+ {file = "wrapt-1.17.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:a9a2203361a6e6404f80b99234fe7fb37d1fc73487b5a78dc1aa5b97201e0f22"},
+ {file = "wrapt-1.17.3-cp38-cp38-win32.whl", hash = "sha256:55cbbc356c2842f39bcc553cf695932e8b30e30e797f961860afb308e6b1bb7c"},
+ {file = "wrapt-1.17.3-cp38-cp38-win_amd64.whl", hash = "sha256:ad85e269fe54d506b240d2d7b9f5f2057c2aa9a2ea5b32c66f8902f768117ed2"},
+ {file = "wrapt-1.17.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:30ce38e66630599e1193798285706903110d4f057aab3168a34b7fdc85569afc"},
+ {file = "wrapt-1.17.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:65d1d00fbfb3ea5f20add88bbc0f815150dbbde3b026e6c24759466c8b5a9ef9"},
+ {file = "wrapt-1.17.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a7c06742645f914f26c7f1fa47b8bc4c91d222f76ee20116c43d5ef0912bba2d"},
+ {file = "wrapt-1.17.3-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7e18f01b0c3e4a07fe6dfdb00e29049ba17eadbc5e7609a2a3a4af83ab7d710a"},
+ {file = "wrapt-1.17.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f5f51a6466667a5a356e6381d362d259125b57f059103dd9fdc8c0cf1d14139"},
+ {file = "wrapt-1.17.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:59923aa12d0157f6b82d686c3fd8e1166fa8cdfb3e17b42ce3b6147ff81528df"},
+ {file = "wrapt-1.17.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:46acc57b331e0b3bcb3e1ca3b421d65637915cfcd65eb783cb2f78a511193f9b"},
+ {file = "wrapt-1.17.3-cp39-cp39-win32.whl", hash = "sha256:3e62d15d3cfa26e3d0788094de7b64efa75f3a53875cdbccdf78547aed547a81"},
+ {file = "wrapt-1.17.3-cp39-cp39-win_amd64.whl", hash = "sha256:1f23fa283f51c890eda8e34e4937079114c74b4c81d2b2f1f1d94948f5cc3d7f"},
+ {file = "wrapt-1.17.3-cp39-cp39-win_arm64.whl", hash = "sha256:24c2ed34dc222ed754247a2702b1e1e89fdbaa4016f324b4b8f1a802d4ffe87f"},
+ {file = "wrapt-1.17.3-py3-none-any.whl", hash = "sha256:7171ae35d2c33d326ac19dd8facb1e82e5fd04ef8c6c0e394d7af55a55051c22"},
+ {file = "wrapt-1.17.3.tar.gz", hash = "sha256:f66eb08feaa410fe4eebd17f2a2c8e2e46d3476e9f8c783daa8e09e0faa666d0"},
+]
+
+[[package]]
+name = "xxhash"
+version = "3.5.0"
+description = "Python binding for xxHash"
+optional = false
+python-versions = ">=3.7"
+groups = ["main"]
+files = [
+ {file = "xxhash-3.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ece616532c499ee9afbb83078b1b952beffef121d989841f7f4b3dc5ac0fd212"},
+ {file = "xxhash-3.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3171f693dbc2cef6477054a665dc255d996646b4023fe56cb4db80e26f4cc520"},
+ {file = "xxhash-3.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7c5d3e570ef46adaf93fc81b44aca6002b5a4d8ca11bd0580c07eac537f36680"},
+ {file = "xxhash-3.5.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7cb29a034301e2982df8b1fe6328a84f4b676106a13e9135a0d7e0c3e9f806da"},
+ {file = "xxhash-3.5.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5d0d307d27099bb0cbeea7260eb39ed4fdb99c5542e21e94bb6fd29e49c57a23"},
+ {file = "xxhash-3.5.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0342aafd421795d740e514bc9858ebddfc705a75a8c5046ac56d85fe97bf196"},
+ {file = "xxhash-3.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3dbbd9892c5ebffeca1ed620cf0ade13eb55a0d8c84e0751a6653adc6ac40d0c"},
+ {file = "xxhash-3.5.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4cc2d67fdb4d057730c75a64c5923abfa17775ae234a71b0200346bfb0a7f482"},
+ {file = "xxhash-3.5.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:ec28adb204b759306a3d64358a5e5c07d7b1dd0ccbce04aa76cb9377b7b70296"},
+ {file = "xxhash-3.5.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:1328f6d8cca2b86acb14104e381225a3d7b42c92c4b86ceae814e5c400dbb415"},
+ {file = "xxhash-3.5.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8d47ebd9f5d9607fd039c1fbf4994e3b071ea23eff42f4ecef246ab2b7334198"},
+ {file = "xxhash-3.5.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b96d559e0fcddd3343c510a0fe2b127fbff16bf346dd76280b82292567523442"},
+ {file = "xxhash-3.5.0-cp310-cp310-win32.whl", hash = "sha256:61c722ed8d49ac9bc26c7071eeaa1f6ff24053d553146d5df031802deffd03da"},
+ {file = "xxhash-3.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:9bed5144c6923cc902cd14bb8963f2d5e034def4486ab0bbe1f58f03f042f9a9"},
+ {file = "xxhash-3.5.0-cp310-cp310-win_arm64.whl", hash = "sha256:893074d651cf25c1cc14e3bea4fceefd67f2921b1bb8e40fcfeba56820de80c6"},
+ {file = "xxhash-3.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:02c2e816896dc6f85922ced60097bcf6f008dedfc5073dcba32f9c8dd786f3c1"},
+ {file = "xxhash-3.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6027dcd885e21581e46d3c7f682cfb2b870942feeed58a21c29583512c3f09f8"},
+ {file = "xxhash-3.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1308fa542bbdbf2fa85e9e66b1077eea3a88bef38ee8a06270b4298a7a62a166"},
+ {file = "xxhash-3.5.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c28b2fdcee797e1c1961cd3bcd3d545cab22ad202c846235197935e1df2f8ef7"},
+ {file = "xxhash-3.5.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:924361811732ddad75ff23e90efd9ccfda4f664132feecb90895bade6a1b4623"},
+ {file = "xxhash-3.5.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89997aa1c4b6a5b1e5b588979d1da048a3c6f15e55c11d117a56b75c84531f5a"},
+ {file = "xxhash-3.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:685c4f4e8c59837de103344eb1c8a3851f670309eb5c361f746805c5471b8c88"},
+ {file = "xxhash-3.5.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:dbd2ecfbfee70bc1a4acb7461fa6af7748ec2ab08ac0fa298f281c51518f982c"},
+ {file = "xxhash-3.5.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:25b5a51dc3dfb20a10833c8eee25903fd2e14059e9afcd329c9da20609a307b2"},
+ {file = "xxhash-3.5.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a8fb786fb754ef6ff8c120cb96629fb518f8eb5a61a16aac3a979a9dbd40a084"},
+ {file = "xxhash-3.5.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:a905ad00ad1e1c34fe4e9d7c1d949ab09c6fa90c919860c1534ff479f40fd12d"},
+ {file = "xxhash-3.5.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:963be41bcd49f53af6d795f65c0da9b4cc518c0dd9c47145c98f61cb464f4839"},
+ {file = "xxhash-3.5.0-cp311-cp311-win32.whl", hash = "sha256:109b436096d0a2dd039c355fa3414160ec4d843dfecc64a14077332a00aeb7da"},
+ {file = "xxhash-3.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:b702f806693201ad6c0a05ddbbe4c8f359626d0b3305f766077d51388a6bac58"},
+ {file = "xxhash-3.5.0-cp311-cp311-win_arm64.whl", hash = "sha256:c4dcb4120d0cc3cc448624147dba64e9021b278c63e34a38789b688fd0da9bf3"},
+ {file = "xxhash-3.5.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:14470ace8bd3b5d51318782cd94e6f94431974f16cb3b8dc15d52f3b69df8e00"},
+ {file = "xxhash-3.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:59aa1203de1cb96dbeab595ded0ad0c0056bb2245ae11fac11c0ceea861382b9"},
+ {file = "xxhash-3.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:08424f6648526076e28fae6ea2806c0a7d504b9ef05ae61d196d571e5c879c84"},
+ {file = "xxhash-3.5.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:61a1ff00674879725b194695e17f23d3248998b843eb5e933007ca743310f793"},
+ {file = "xxhash-3.5.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2f2c61bee5844d41c3eb015ac652a0229e901074951ae48581d58bfb2ba01be"},
+ {file = "xxhash-3.5.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d32a592cac88d18cc09a89172e1c32d7f2a6e516c3dfde1b9adb90ab5df54a6"},
+ {file = "xxhash-3.5.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:70dabf941dede727cca579e8c205e61121afc9b28516752fd65724be1355cc90"},
+ {file = "xxhash-3.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e5d0ddaca65ecca9c10dcf01730165fd858533d0be84c75c327487c37a906a27"},
+ {file = "xxhash-3.5.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3e5b5e16c5a480fe5f59f56c30abdeba09ffd75da8d13f6b9b6fd224d0b4d0a2"},
+ {file = "xxhash-3.5.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:149b7914451eb154b3dfaa721315117ea1dac2cc55a01bfbd4df7c68c5dd683d"},
+ {file = "xxhash-3.5.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:eade977f5c96c677035ff39c56ac74d851b1cca7d607ab3d8f23c6b859379cab"},
+ {file = "xxhash-3.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fa9f547bd98f5553d03160967866a71056a60960be00356a15ecc44efb40ba8e"},
+ {file = "xxhash-3.5.0-cp312-cp312-win32.whl", hash = "sha256:f7b58d1fd3551b8c80a971199543379be1cee3d0d409e1f6d8b01c1a2eebf1f8"},
+ {file = "xxhash-3.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:fa0cafd3a2af231b4e113fba24a65d7922af91aeb23774a8b78228e6cd785e3e"},
+ {file = "xxhash-3.5.0-cp312-cp312-win_arm64.whl", hash = "sha256:586886c7e89cb9828bcd8a5686b12e161368e0064d040e225e72607b43858ba2"},
+ {file = "xxhash-3.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:37889a0d13b0b7d739cfc128b1c902f04e32de17b33d74b637ad42f1c55101f6"},
+ {file = "xxhash-3.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:97a662338797c660178e682f3bc180277b9569a59abfb5925e8620fba00b9fc5"},
+ {file = "xxhash-3.5.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7f85e0108d51092bdda90672476c7d909c04ada6923c14ff9d913c4f7dc8a3bc"},
+ {file = "xxhash-3.5.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cd2fd827b0ba763ac919440042302315c564fdb797294d86e8cdd4578e3bc7f3"},
+ {file = "xxhash-3.5.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:82085c2abec437abebf457c1d12fccb30cc8b3774a0814872511f0f0562c768c"},
+ {file = "xxhash-3.5.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07fda5de378626e502b42b311b049848c2ef38784d0d67b6f30bb5008642f8eb"},
+ {file = "xxhash-3.5.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c279f0d2b34ef15f922b77966640ade58b4ccdfef1c4d94b20f2a364617a493f"},
+ {file = "xxhash-3.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:89e66ceed67b213dec5a773e2f7a9e8c58f64daeb38c7859d8815d2c89f39ad7"},
+ {file = "xxhash-3.5.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:bcd51708a633410737111e998ceb3b45d3dbc98c0931f743d9bb0a209033a326"},
+ {file = "xxhash-3.5.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3ff2c0a34eae7df88c868be53a8dd56fbdf592109e21d4bfa092a27b0bf4a7bf"},
+ {file = "xxhash-3.5.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:4e28503dccc7d32e0b9817aa0cbfc1f45f563b2c995b7a66c4c8a0d232e840c7"},
+ {file = "xxhash-3.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a6c50017518329ed65a9e4829154626f008916d36295b6a3ba336e2458824c8c"},
+ {file = "xxhash-3.5.0-cp313-cp313-win32.whl", hash = "sha256:53a068fe70301ec30d868ece566ac90d873e3bb059cf83c32e76012c889b8637"},
+ {file = "xxhash-3.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:80babcc30e7a1a484eab952d76a4f4673ff601f54d5142c26826502740e70b43"},
+ {file = "xxhash-3.5.0-cp313-cp313-win_arm64.whl", hash = "sha256:4811336f1ce11cac89dcbd18f3a25c527c16311709a89313c3acaf771def2d4b"},
+ {file = "xxhash-3.5.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6e5f70f6dca1d3b09bccb7daf4e087075ff776e3da9ac870f86ca316736bb4aa"},
+ {file = "xxhash-3.5.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e76e83efc7b443052dd1e585a76201e40b3411fe3da7af4fe434ec51b2f163b"},
+ {file = "xxhash-3.5.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:33eac61d0796ca0591f94548dcfe37bb193671e0c9bcf065789b5792f2eda644"},
+ {file = "xxhash-3.5.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ec70a89be933ea49222fafc3999987d7899fc676f688dd12252509434636622"},
+ {file = "xxhash-3.5.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86b8e7f703ec6ff4f351cfdb9f428955859537125904aa8c963604f2e9d3e7"},
+ {file = "xxhash-3.5.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0adfbd36003d9f86c8c97110039f7539b379f28656a04097e7434d3eaf9aa131"},
+ {file = "xxhash-3.5.0-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:63107013578c8a730419adc05608756c3fa640bdc6abe806c3123a49fb829f43"},
+ {file = "xxhash-3.5.0-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:683b94dbd1ca67557850b86423318a2e323511648f9f3f7b1840408a02b9a48c"},
+ {file = "xxhash-3.5.0-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:5d2a01dcce81789cf4b12d478b5464632204f4c834dc2d064902ee27d2d1f0ee"},
+ {file = "xxhash-3.5.0-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:a9d360a792cbcce2fe7b66b8d51274ec297c53cbc423401480e53b26161a290d"},
+ {file = "xxhash-3.5.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:f0b48edbebea1b7421a9c687c304f7b44d0677c46498a046079d445454504737"},
+ {file = "xxhash-3.5.0-cp37-cp37m-win32.whl", hash = "sha256:7ccb800c9418e438b44b060a32adeb8393764da7441eb52aa2aa195448935306"},
+ {file = "xxhash-3.5.0-cp37-cp37m-win_amd64.whl", hash = "sha256:c3bc7bf8cb8806f8d1c9bf149c18708cb1c406520097d6b0a73977460ea03602"},
+ {file = "xxhash-3.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:74752ecaa544657d88b1d1c94ae68031e364a4d47005a90288f3bab3da3c970f"},
+ {file = "xxhash-3.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:dee1316133c9b463aa81aca676bc506d3f80d8f65aeb0bba2b78d0b30c51d7bd"},
+ {file = "xxhash-3.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:602d339548d35a8579c6b013339fb34aee2df9b4e105f985443d2860e4d7ffaa"},
+ {file = "xxhash-3.5.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:695735deeddfb35da1677dbc16a083445360e37ff46d8ac5c6fcd64917ff9ade"},
+ {file = "xxhash-3.5.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1030a39ba01b0c519b1a82f80e8802630d16ab95dc3f2b2386a0b5c8ed5cbb10"},
+ {file = "xxhash-3.5.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a5bc08f33c4966f4eb6590d6ff3ceae76151ad744576b5fc6c4ba8edd459fdec"},
+ {file = "xxhash-3.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:160e0c19ee500482ddfb5d5570a0415f565d8ae2b3fd69c5dcfce8a58107b1c3"},
+ {file = "xxhash-3.5.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:f1abffa122452481a61c3551ab3c89d72238e279e517705b8b03847b1d93d738"},
+ {file = "xxhash-3.5.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:d5e9db7ef3ecbfc0b4733579cea45713a76852b002cf605420b12ef3ef1ec148"},
+ {file = "xxhash-3.5.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:23241ff6423378a731d84864bf923a41649dc67b144debd1077f02e6249a0d54"},
+ {file = "xxhash-3.5.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:82b833d5563fefd6fceafb1aed2f3f3ebe19f84760fdd289f8b926731c2e6e91"},
+ {file = "xxhash-3.5.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0a80ad0ffd78bef9509eee27b4a29e56f5414b87fb01a888353e3d5bda7038bd"},
+ {file = "xxhash-3.5.0-cp38-cp38-win32.whl", hash = "sha256:50ac2184ffb1b999e11e27c7e3e70cc1139047e7ebc1aa95ed12f4269abe98d4"},
+ {file = "xxhash-3.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:392f52ebbb932db566973693de48f15ce787cabd15cf6334e855ed22ea0be5b3"},
+ {file = "xxhash-3.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bfc8cdd7f33d57f0468b0614ae634cc38ab9202c6957a60e31d285a71ebe0301"},
+ {file = "xxhash-3.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e0c48b6300cd0b0106bf49169c3e0536408dfbeb1ccb53180068a18b03c662ab"},
+ {file = "xxhash-3.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fe1a92cfbaa0a1253e339ccec42dbe6db262615e52df591b68726ab10338003f"},
+ {file = "xxhash-3.5.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:33513d6cc3ed3b559134fb307aae9bdd94d7e7c02907b37896a6c45ff9ce51bd"},
+ {file = "xxhash-3.5.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eefc37f6138f522e771ac6db71a6d4838ec7933939676f3753eafd7d3f4c40bc"},
+ {file = "xxhash-3.5.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a606c8070ada8aa2a88e181773fa1ef17ba65ce5dd168b9d08038e2a61b33754"},
+ {file = "xxhash-3.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:42eca420c8fa072cc1dd62597635d140e78e384a79bb4944f825fbef8bfeeef6"},
+ {file = "xxhash-3.5.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:604253b2143e13218ff1ef0b59ce67f18b8bd1c4205d2ffda22b09b426386898"},
+ {file = "xxhash-3.5.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:6e93a5ad22f434d7876665444a97e713a8f60b5b1a3521e8df11b98309bff833"},
+ {file = "xxhash-3.5.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:7a46e1d6d2817ba8024de44c4fd79913a90e5f7265434cef97026215b7d30df6"},
+ {file = "xxhash-3.5.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:30eb2efe6503c379b7ab99c81ba4a779748e3830241f032ab46bd182bf5873af"},
+ {file = "xxhash-3.5.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c8aa771ff2c13dd9cda8166d685d7333d389fae30a4d2bb39d63ab5775de8606"},
+ {file = "xxhash-3.5.0-cp39-cp39-win32.whl", hash = "sha256:5ed9ebc46f24cf91034544b26b131241b699edbfc99ec5e7f8f3d02d6eb7fba4"},
+ {file = "xxhash-3.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:220f3f896c6b8d0316f63f16c077d52c412619e475f9372333474ee15133a558"},
+ {file = "xxhash-3.5.0-cp39-cp39-win_arm64.whl", hash = "sha256:a7b1d8315d9b5e9f89eb2933b73afae6ec9597a258d52190944437158b49d38e"},
+ {file = "xxhash-3.5.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:2014c5b3ff15e64feecb6b713af12093f75b7926049e26a580e94dcad3c73d8c"},
+ {file = "xxhash-3.5.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fab81ef75003eda96239a23eda4e4543cedc22e34c373edcaf744e721a163986"},
+ {file = "xxhash-3.5.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e2febf914ace002132aa09169cc572e0d8959d0f305f93d5828c4836f9bc5a6"},
+ {file = "xxhash-3.5.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5d3a10609c51da2a1c0ea0293fc3968ca0a18bd73838455b5bca3069d7f8e32b"},
+ {file = "xxhash-3.5.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5a74f23335b9689b66eb6dbe2a931a88fcd7a4c2cc4b1cb0edba8ce381c7a1da"},
+ {file = "xxhash-3.5.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:2b4154c00eb22e4d543f472cfca430e7962a0f1d0f3778334f2e08a7ba59363c"},
+ {file = "xxhash-3.5.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d30bbc1644f726b825b3278764240f449d75f1a8bdda892e641d4a688b1494ae"},
+ {file = "xxhash-3.5.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fa0b72f2423e2aa53077e54a61c28e181d23effeaafd73fcb9c494e60930c8e"},
+ {file = "xxhash-3.5.0-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:13de2b76c1835399b2e419a296d5b38dc4855385d9e96916299170085ef72f57"},
+ {file = "xxhash-3.5.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:0691bfcc4f9c656bcb96cc5db94b4d75980b9d5589f2e59de790091028580837"},
+ {file = "xxhash-3.5.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:297595fe6138d4da2c8ce9e72a04d73e58725bb60f3a19048bc96ab2ff31c692"},
+ {file = "xxhash-3.5.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc1276d369452040cbb943300dc8abeedab14245ea44056a2943183822513a18"},
+ {file = "xxhash-3.5.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2061188a1ba352fc699c82bff722f4baacb4b4b8b2f0c745d2001e56d0dfb514"},
+ {file = "xxhash-3.5.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:38c384c434021e4f62b8d9ba0bc9467e14d394893077e2c66d826243025e1f81"},
+ {file = "xxhash-3.5.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:e6a4dd644d72ab316b580a1c120b375890e4c52ec392d4aef3c63361ec4d77d1"},
+ {file = "xxhash-3.5.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:531af8845aaadcadf951b7e0c1345c6b9c68a990eeb74ff9acd8501a0ad6a1c9"},
+ {file = "xxhash-3.5.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ce379bcaa9fcc00f19affa7773084dd09f5b59947b3fb47a1ceb0179f91aaa1"},
+ {file = "xxhash-3.5.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd1b2281d01723f076df3c8188f43f2472248a6b63118b036e641243656b1b0f"},
+ {file = "xxhash-3.5.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9c770750cc80e8694492244bca7251385188bc5597b6a39d98a9f30e8da984e0"},
+ {file = "xxhash-3.5.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:b150b8467852e1bd844387459aa6fbe11d7f38b56e901f9f3b3e6aba0d660240"},
+ {file = "xxhash-3.5.0.tar.gz", hash = "sha256:84f2caddf951c9cbf8dc2e22a89d4ccf5d86391ac6418fe81e3c67d0cf60b45f"},
+]
+
+[[package]]
+name = "yarl"
+version = "1.20.1"
+description = "Yet another URL library"
+optional = false
+python-versions = ">=3.9"
+groups = ["main"]
+files = [
+ {file = "yarl-1.20.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6032e6da6abd41e4acda34d75a816012717000fa6839f37124a47fcefc49bec4"},
+ {file = "yarl-1.20.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2c7b34d804b8cf9b214f05015c4fee2ebe7ed05cf581e7192c06555c71f4446a"},
+ {file = "yarl-1.20.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0c869f2651cc77465f6cd01d938d91a11d9ea5d798738c1dc077f3de0b5e5fed"},
+ {file = "yarl-1.20.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62915e6688eb4d180d93840cda4110995ad50c459bf931b8b3775b37c264af1e"},
+ {file = "yarl-1.20.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:41ebd28167bc6af8abb97fec1a399f412eec5fd61a3ccbe2305a18b84fb4ca73"},
+ {file = "yarl-1.20.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:21242b4288a6d56f04ea193adde174b7e347ac46ce6bc84989ff7c1b1ecea84e"},
+ {file = "yarl-1.20.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bea21cdae6c7eb02ba02a475f37463abfe0a01f5d7200121b03e605d6a0439f8"},
+ {file = "yarl-1.20.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f8a891e4a22a89f5dde7862994485e19db246b70bb288d3ce73a34422e55b23"},
+ {file = "yarl-1.20.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dd803820d44c8853a109a34e3660e5a61beae12970da479cf44aa2954019bf70"},
+ {file = "yarl-1.20.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b982fa7f74c80d5c0c7b5b38f908971e513380a10fecea528091405f519b9ebb"},
+ {file = "yarl-1.20.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:33f29ecfe0330c570d997bcf1afd304377f2e48f61447f37e846a6058a4d33b2"},
+ {file = "yarl-1.20.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:835ab2cfc74d5eb4a6a528c57f05688099da41cf4957cf08cad38647e4a83b30"},
+ {file = "yarl-1.20.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:46b5e0ccf1943a9a6e766b2c2b8c732c55b34e28be57d8daa2b3c1d1d4009309"},
+ {file = "yarl-1.20.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:df47c55f7d74127d1b11251fe6397d84afdde0d53b90bedb46a23c0e534f9d24"},
+ {file = "yarl-1.20.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:76d12524d05841276b0e22573f28d5fbcb67589836772ae9244d90dd7d66aa13"},
+ {file = "yarl-1.20.1-cp310-cp310-win32.whl", hash = "sha256:6c4fbf6b02d70e512d7ade4b1f998f237137f1417ab07ec06358ea04f69134f8"},
+ {file = "yarl-1.20.1-cp310-cp310-win_amd64.whl", hash = "sha256:aef6c4d69554d44b7f9d923245f8ad9a707d971e6209d51279196d8e8fe1ae16"},
+ {file = "yarl-1.20.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:47ee6188fea634bdfaeb2cc420f5b3b17332e6225ce88149a17c413c77ff269e"},
+ {file = "yarl-1.20.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d0f6500f69e8402d513e5eedb77a4e1818691e8f45e6b687147963514d84b44b"},
+ {file = "yarl-1.20.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7a8900a42fcdaad568de58887c7b2f602962356908eedb7628eaf6021a6e435b"},
+ {file = "yarl-1.20.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bad6d131fda8ef508b36be3ece16d0902e80b88ea7200f030a0f6c11d9e508d4"},
+ {file = "yarl-1.20.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:df018d92fe22aaebb679a7f89fe0c0f368ec497e3dda6cb81a567610f04501f1"},
+ {file = "yarl-1.20.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8f969afbb0a9b63c18d0feecf0db09d164b7a44a053e78a7d05f5df163e43833"},
+ {file = "yarl-1.20.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:812303eb4aa98e302886ccda58d6b099e3576b1b9276161469c25803a8db277d"},
+ {file = "yarl-1.20.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98c4a7d166635147924aa0bf9bfe8d8abad6fffa6102de9c99ea04a1376f91e8"},
+ {file = "yarl-1.20.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:12e768f966538e81e6e7550f9086a6236b16e26cd964cf4df35349970f3551cf"},
+ {file = "yarl-1.20.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fe41919b9d899661c5c28a8b4b0acf704510b88f27f0934ac7a7bebdd8938d5e"},
+ {file = "yarl-1.20.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:8601bc010d1d7780592f3fc1bdc6c72e2b6466ea34569778422943e1a1f3c389"},
+ {file = "yarl-1.20.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:daadbdc1f2a9033a2399c42646fbd46da7992e868a5fe9513860122d7fe7a73f"},
+ {file = "yarl-1.20.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:03aa1e041727cb438ca762628109ef1333498b122e4c76dd858d186a37cec845"},
+ {file = "yarl-1.20.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:642980ef5e0fa1de5fa96d905c7e00cb2c47cb468bfcac5a18c58e27dbf8d8d1"},
+ {file = "yarl-1.20.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:86971e2795584fe8c002356d3b97ef6c61862720eeff03db2a7c86b678d85b3e"},
+ {file = "yarl-1.20.1-cp311-cp311-win32.whl", hash = "sha256:597f40615b8d25812f14562699e287f0dcc035d25eb74da72cae043bb884d773"},
+ {file = "yarl-1.20.1-cp311-cp311-win_amd64.whl", hash = "sha256:26ef53a9e726e61e9cd1cda6b478f17e350fb5800b4bd1cd9fe81c4d91cfeb2e"},
+ {file = "yarl-1.20.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bdcc4cd244e58593a4379fe60fdee5ac0331f8eb70320a24d591a3be197b94a9"},
+ {file = "yarl-1.20.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b29a2c385a5f5b9c7d9347e5812b6f7ab267193c62d282a540b4fc528c8a9d2a"},
+ {file = "yarl-1.20.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1112ae8154186dfe2de4732197f59c05a83dc814849a5ced892b708033f40dc2"},
+ {file = "yarl-1.20.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:90bbd29c4fe234233f7fa2b9b121fb63c321830e5d05b45153a2ca68f7d310ee"},
+ {file = "yarl-1.20.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:680e19c7ce3710ac4cd964e90dad99bf9b5029372ba0c7cbfcd55e54d90ea819"},
+ {file = "yarl-1.20.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4a979218c1fdb4246a05efc2cc23859d47c89af463a90b99b7c56094daf25a16"},
+ {file = "yarl-1.20.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:255b468adf57b4a7b65d8aad5b5138dce6a0752c139965711bdcb81bc370e1b6"},
+ {file = "yarl-1.20.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a97d67108e79cfe22e2b430d80d7571ae57d19f17cda8bb967057ca8a7bf5bfd"},
+ {file = "yarl-1.20.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8570d998db4ddbfb9a590b185a0a33dbf8aafb831d07a5257b4ec9948df9cb0a"},
+ {file = "yarl-1.20.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:97c75596019baae7c71ccf1d8cc4738bc08134060d0adfcbe5642f778d1dca38"},
+ {file = "yarl-1.20.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:1c48912653e63aef91ff988c5432832692ac5a1d8f0fb8a33091520b5bbe19ef"},
+ {file = "yarl-1.20.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4c3ae28f3ae1563c50f3d37f064ddb1511ecc1d5584e88c6b7c63cf7702a6d5f"},
+ {file = "yarl-1.20.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c5e9642f27036283550f5f57dc6156c51084b458570b9d0d96100c8bebb186a8"},
+ {file = "yarl-1.20.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:2c26b0c49220d5799f7b22c6838409ee9bc58ee5c95361a4d7831f03cc225b5a"},
+ {file = "yarl-1.20.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:564ab3d517e3d01c408c67f2e5247aad4019dcf1969982aba3974b4093279004"},
+ {file = "yarl-1.20.1-cp312-cp312-win32.whl", hash = "sha256:daea0d313868da1cf2fac6b2d3a25c6e3a9e879483244be38c8e6a41f1d876a5"},
+ {file = "yarl-1.20.1-cp312-cp312-win_amd64.whl", hash = "sha256:48ea7d7f9be0487339828a4de0360d7ce0efc06524a48e1810f945c45b813698"},
+ {file = "yarl-1.20.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:0b5ff0fbb7c9f1b1b5ab53330acbfc5247893069e7716840c8e7d5bb7355038a"},
+ {file = "yarl-1.20.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:14f326acd845c2b2e2eb38fb1346c94f7f3b01a4f5c788f8144f9b630bfff9a3"},
+ {file = "yarl-1.20.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f60e4ad5db23f0b96e49c018596707c3ae89f5d0bd97f0ad3684bcbad899f1e7"},
+ {file = "yarl-1.20.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:49bdd1b8e00ce57e68ba51916e4bb04461746e794e7c4d4bbc42ba2f18297691"},
+ {file = "yarl-1.20.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:66252d780b45189975abfed839616e8fd2dbacbdc262105ad7742c6ae58f3e31"},
+ {file = "yarl-1.20.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59174e7332f5d153d8f7452a102b103e2e74035ad085f404df2e40e663a22b28"},
+ {file = "yarl-1.20.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e3968ec7d92a0c0f9ac34d5ecfd03869ec0cab0697c91a45db3fbbd95fe1b653"},
+ {file = "yarl-1.20.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1a4fbb50e14396ba3d375f68bfe02215d8e7bc3ec49da8341fe3157f59d2ff5"},
+ {file = "yarl-1.20.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:11a62c839c3a8eac2410e951301309426f368388ff2f33799052787035793b02"},
+ {file = "yarl-1.20.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:041eaa14f73ff5a8986b4388ac6bb43a77f2ea09bf1913df7a35d4646db69e53"},
+ {file = "yarl-1.20.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:377fae2fef158e8fd9d60b4c8751387b8d1fb121d3d0b8e9b0be07d1b41e83dc"},
+ {file = "yarl-1.20.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:1c92f4390e407513f619d49319023664643d3339bd5e5a56a3bebe01bc67ec04"},
+ {file = "yarl-1.20.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d25ddcf954df1754ab0f86bb696af765c5bfaba39b74095f27eececa049ef9a4"},
+ {file = "yarl-1.20.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:909313577e9619dcff8c31a0ea2aa0a2a828341d92673015456b3ae492e7317b"},
+ {file = "yarl-1.20.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:793fd0580cb9664548c6b83c63b43c477212c0260891ddf86809e1c06c8b08f1"},
+ {file = "yarl-1.20.1-cp313-cp313-win32.whl", hash = "sha256:468f6e40285de5a5b3c44981ca3a319a4b208ccc07d526b20b12aeedcfa654b7"},
+ {file = "yarl-1.20.1-cp313-cp313-win_amd64.whl", hash = "sha256:495b4ef2fea40596bfc0affe3837411d6aa3371abcf31aac0ccc4bdd64d4ef5c"},
+ {file = "yarl-1.20.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:f60233b98423aab21d249a30eb27c389c14929f47be8430efa7dbd91493a729d"},
+ {file = "yarl-1.20.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:6f3eff4cc3f03d650d8755c6eefc844edde99d641d0dcf4da3ab27141a5f8ddf"},
+ {file = "yarl-1.20.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:69ff8439d8ba832d6bed88af2c2b3445977eba9a4588b787b32945871c2444e3"},
+ {file = "yarl-1.20.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cf34efa60eb81dd2645a2e13e00bb98b76c35ab5061a3989c7a70f78c85006d"},
+ {file = "yarl-1.20.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8e0fe9364ad0fddab2688ce72cb7a8e61ea42eff3c7caeeb83874a5d479c896c"},
+ {file = "yarl-1.20.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8f64fbf81878ba914562c672024089e3401974a39767747691c65080a67b18c1"},
+ {file = "yarl-1.20.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f6342d643bf9a1de97e512e45e4b9560a043347e779a173250824f8b254bd5ce"},
+ {file = "yarl-1.20.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56dac5f452ed25eef0f6e3c6a066c6ab68971d96a9fb441791cad0efba6140d3"},
+ {file = "yarl-1.20.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7d7f497126d65e2cad8dc5f97d34c27b19199b6414a40cb36b52f41b79014be"},
+ {file = "yarl-1.20.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:67e708dfb8e78d8a19169818eeb5c7a80717562de9051bf2413aca8e3696bf16"},
+ {file = "yarl-1.20.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:595c07bc79af2494365cc96ddeb772f76272364ef7c80fb892ef9d0649586513"},
+ {file = "yarl-1.20.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:7bdd2f80f4a7df852ab9ab49484a4dee8030023aa536df41f2d922fd57bf023f"},
+ {file = "yarl-1.20.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:c03bfebc4ae8d862f853a9757199677ab74ec25424d0ebd68a0027e9c639a390"},
+ {file = "yarl-1.20.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:344d1103e9c1523f32a5ed704d576172d2cabed3122ea90b1d4e11fe17c66458"},
+ {file = "yarl-1.20.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:88cab98aa4e13e1ade8c141daeedd300a4603b7132819c484841bb7af3edce9e"},
+ {file = "yarl-1.20.1-cp313-cp313t-win32.whl", hash = "sha256:b121ff6a7cbd4abc28985b6028235491941b9fe8fe226e6fdc539c977ea1739d"},
+ {file = "yarl-1.20.1-cp313-cp313t-win_amd64.whl", hash = "sha256:541d050a355bbbc27e55d906bc91cb6fe42f96c01413dd0f4ed5a5240513874f"},
+ {file = "yarl-1.20.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e42ba79e2efb6845ebab49c7bf20306c4edf74a0b20fc6b2ccdd1a219d12fad3"},
+ {file = "yarl-1.20.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:41493b9b7c312ac448b7f0a42a089dffe1d6e6e981a2d76205801a023ed26a2b"},
+ {file = "yarl-1.20.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f5a5928ff5eb13408c62a968ac90d43f8322fd56d87008b8f9dabf3c0f6ee983"},
+ {file = "yarl-1.20.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:30c41ad5d717b3961b2dd785593b67d386b73feca30522048d37298fee981805"},
+ {file = "yarl-1.20.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:59febc3969b0781682b469d4aca1a5cab7505a4f7b85acf6db01fa500fa3f6ba"},
+ {file = "yarl-1.20.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d2b6fb3622b7e5bf7a6e5b679a69326b4279e805ed1699d749739a61d242449e"},
+ {file = "yarl-1.20.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:749d73611db8d26a6281086f859ea7ec08f9c4c56cec864e52028c8b328db723"},
+ {file = "yarl-1.20.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9427925776096e664c39e131447aa20ec738bdd77c049c48ea5200db2237e000"},
+ {file = "yarl-1.20.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff70f32aa316393eaf8222d518ce9118148eddb8a53073c2403863b41033eed5"},
+ {file = "yarl-1.20.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:c7ddf7a09f38667aea38801da8b8d6bfe81df767d9dfc8c88eb45827b195cd1c"},
+ {file = "yarl-1.20.1-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:57edc88517d7fc62b174fcfb2e939fbc486a68315d648d7e74d07fac42cec240"},
+ {file = "yarl-1.20.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:dab096ce479d5894d62c26ff4f699ec9072269d514b4edd630a393223f45a0ee"},
+ {file = "yarl-1.20.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:14a85f3bd2d7bb255be7183e5d7d6e70add151a98edf56a770d6140f5d5f4010"},
+ {file = "yarl-1.20.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:2c89b5c792685dd9cd3fa9761c1b9f46fc240c2a3265483acc1565769996a3f8"},
+ {file = "yarl-1.20.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:69e9b141de5511021942a6866990aea6d111c9042235de90e08f94cf972ca03d"},
+ {file = "yarl-1.20.1-cp39-cp39-win32.whl", hash = "sha256:b5f307337819cdfdbb40193cad84978a029f847b0a357fbe49f712063cfc4f06"},
+ {file = "yarl-1.20.1-cp39-cp39-win_amd64.whl", hash = "sha256:eae7bfe2069f9c1c5b05fc7fe5d612e5bbc089a39309904ee8b829e322dcad00"},
+ {file = "yarl-1.20.1-py3-none-any.whl", hash = "sha256:83b8eb083fe4683c6115795d9fc1cfaf2cbbefb19b3a1cb68f6527460f483a77"},
+ {file = "yarl-1.20.1.tar.gz", hash = "sha256:d017a4997ee50c91fd5466cef416231bb82177b93b029906cefc542ce14c35ac"},
+]
+
+[package.dependencies]
+idna = ">=2.0"
+multidict = ">=4.0"
+propcache = ">=0.2.1"
+
+[[package]]
+name = "zipp"
+version = "3.23.0"
+description = "Backport of pathlib-compatible object wrapper for zip files"
+optional = false
+python-versions = ">=3.9"
+groups = ["main", "docs"]
+markers = "python_version == \"3.9\""
+files = [
+ {file = "zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e"},
+ {file = "zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166"},
+]
+
+[package.extras]
+check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""]
+cover = ["pytest-cov"]
+doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
+enabler = ["pytest-enabler (>=2.2)"]
+test = ["big-O", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more_itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"]
+type = ["pytest-mypy"]
+
+[metadata]
+lock-version = "2.1"
+python-versions = ">=3.9,<4.0"
+content-hash = "5f7a95ad5122825bf0fb6ab75c9390a1f257b32404bea15bc611e07372ca33d9"
diff --git a/pyproject.toml b/pyproject.toml
index 3bcc130d..9b94b8b9 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,17 +1,18 @@
[build-system]
-requires = ["setuptools>=61.0", "wheel"]
-build-backend = "setuptools.build_meta"
+requires = ["poetry-core"]
+build-backend = "poetry.core.masonry.api"
-[project]
+[tool.poetry]
name = "sparse-attention-hub"
version = "0.1.0"
-description = "A comprehensive framework for sparse attention mechanisms"
-authors = [
- {name = "AlexCuadron", email = "alex.cl.2000@gmail.com"}
-]
+description = "A framework for implementing and evaluating sparse attention mechanisms"
+authors = ["Aditya Desai ", "AlexCuadron ", "Kumar Krishna Agrawal ", "Luis Gaspar Schroeder "]
readme = "README.md"
-license = {text = "MIT"}
-requires-python = ">=3.9"
+license = "MIT"
+homepage = "https://github.com/xAlg-ai/sparse-attention-hub"
+repository = "https://github.com/xAlg-ai/sparse-attention-hub"
+documentation = "https://sparse-attention-hub.readthedocs.io"
+keywords = ["attention", "sparse", "transformer", "deep-learning", "pytorch"]
classifiers = [
"Development Status :: 3 - Alpha",
"Intended Audience :: Developers",
@@ -19,86 +20,75 @@ classifiers = [
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
"Programming Language :: Python :: 3",
- "Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Topic :: Scientific/Engineering :: Artificial Intelligence",
"Topic :: Software Development :: Libraries :: Python Modules",
]
-keywords = ["attention", "sparse", "transformer", "deep-learning", "pytorch"]
+packages = [{include = "sparse_attention_hub"}]
-dependencies = [
- "torch>=1.9.0",
- "numpy>=1.21.0",
- "matplotlib>=3.5.0",
- "seaborn>=0.11.0",
- "datasets>=2.0.0",
- "transformers>=4.20.0",
- "scikit-learn>=1.0.0",
- "rouge>=1.0.0",
- "nltk>=3.8.0",
- "bert-score>=0.3.0",
- "jieba>=0.42.0",
- "hf-transfer>=0.1.9",
- "fuzzywuzzy>=0.18.0",
- "pip>=25.1.1",
- "pytest>=8.4.1",
- "psutil>=7.0.0",
- "plotly>=6.2.0",
- "pandas>=2.3.1",
- "pynvml>=12.0.0",
- "colorama>=0.4.6",
-]
+[tool.poetry.dependencies]
+python = ">=3.9,<4.0"
+torch = ">=1.9.0"
+numpy = ">=1.21.0"
+matplotlib = ">=3.5.0"
+seaborn = ">=0.11.0"
+datasets = ">=2.0.0"
+transformers = ">=4.20.0"
+scikit-learn = ">=1.0.0"
+rouge = ">=1.0.0"
+nltk = ">=3.8.0"
+bert-score = ">=0.3.0"
+jieba = ">=0.42.0"
+hf-transfer = ">=0.1.9"
+fuzzywuzzy = ">=0.18.0"
+psutil = ">=7.0.0"
+plotly = ">=6.2.0"
+pandas = ">=2.3.1"
+pynvml = ">=12.0.0"
+colorama = ">=0.4.6"
-[project.optional-dependencies]
-dev = [
- # Testing
- "pytest>=7.4.0",
- "pytest-cov>=4.1.0",
- "pytest-xdist>=3.3.1",
- "pytest-mock>=3.11.1",
- "mock>=5.2.0",
- # Linting and formatting
- "black==22.10.0",
- "isort==5.12.0",
- "flake8==5.0.4",
- "pylint==2.17.4",
- "mypy==1.4.0",
- "bandit>=1.7.5",
- "pyupgrade>=3.10.1",
- "docformatter>=1.7.5",
- # Pre-commit hooks
- "pre-commit>=3.3.3",
- # Type stubs
- "types-PyYAML>=6.0.12.11",
- "types-requests>=2.31.0.2",
- "types-setuptools>=68.0.0.3",
-]
-docs = [
- "sphinx>=5.0.0",
- "sphinx-rtd-theme>=1.0.0",
- "myst-parser>=0.18.0",
-]
-benchmarks = [
- "wandb>=0.13.0",
- "tqdm>=4.64.0",
- "pandas>=1.5.0",
-]
+[tool.poetry.group.dev.dependencies]
+# Testing
+pytest = ">=7.4.0"
+pytest-cov = ">=4.1.0"
+pytest-xdist = ">=3.3.1"
+pytest-mock = ">=3.11.1"
+mock = ">=5.2.0"
+# Linting and formatting
+black = "22.10.0"
+isort = "5.12.0"
+flake8 = "5.0.4"
+pylint = "2.17.4"
+mypy = "1.4.0"
+bandit = ">=1.7.5"
+pyupgrade = ">=3.10.1"
+docformatter = ">=1.7.5"
+# Pre-commit hooks
+pre-commit = ">=3.3.3"
+# Type stubs
+types-PyYAML = ">=6.0.12.11"
+types-requests = ">=2.31.0.2"
+types-setuptools = ">=68.0.0.3"
-[project.urls]
-Homepage = "https://github.com/xAlg-ai/sparse-attention-hub"
-Repository = "https://github.com/xAlg-ai/sparse-attention-hub"
-Documentation = "https://sparse-attention-hub.readthedocs.io"
-"Bug Tracker" = "https://github.com/xAlg-ai/sparse-attention-hub/issues"
+[tool.poetry.group.docs]
+optional = true
-[project.scripts]
-sparse-attention-benchmark = "sparse_attention_hub.benchmark.executor:main"
+[tool.poetry.group.docs.dependencies]
+sphinx = ">=5.0.0"
+sphinx-rtd-theme = ">=1.0.0"
+myst-parser = ">=0.18.0"
+
+[tool.poetry.group.benchmarks]
+optional = true
-[tool.setuptools.packages.find]
-where = ["."]
-include = ["sparse_attention_hub*"]
-exclude = ["tests*"]
+[tool.poetry.group.benchmarks.dependencies]
+wandb = ">=0.13.0"
+tqdm = ">=4.64.0"
+
+[tool.poetry.scripts]
+sparse-attention-benchmark = "sparse_attention_hub.benchmark.executor:main"
[tool.pytest.ini_options]
testpaths = ["tests"]
@@ -120,7 +110,7 @@ markers = [
[tool.black]
line-length = 88
-target-version = ["py38", "py39", "py310", "py311"]
+target-version = ["py39", "py310", "py311"]
include = '\.pyi?$'
extend-exclude = '''
/(
@@ -143,7 +133,7 @@ line_length = 88
known_first_party = ["sparse_attention_hub"]
[tool.mypy]
-python_version = "3.8"
+python_version = "3.9"
warn_return_any = true
warn_unused_configs = true
disallow_untyped_defs = true
@@ -156,6 +146,7 @@ warn_unused_ignores = true
warn_no_return = true
warn_unreachable = true
strict_equality = true
+
[[tool.mypy.overrides]]
module = [
"torch.*",
@@ -166,4 +157,4 @@ module = [
"wandb.*",
]
ignore_missing_imports = true
-ignore_errors = true
+ignore_errors = true
\ No newline at end of file
diff --git a/requirements-dev.txt b/requirements-dev.txt
deleted file mode 100644
index 424422ea..00000000
--- a/requirements-dev.txt
+++ /dev/null
@@ -1,38 +0,0 @@
-# Development dependencies for sparse-attention-hub
-# Linting and formatting
-black==22.10.0
-isort==5.12.0
-flake8==5.0.4
-pylint==2.17.4
-mypy==1.4.0
-bandit==1.7.5
-pyupgrade==3.10.1
-docformatter==1.7.5
-
-# Testing
-pytest==7.4.0
-pytest-cov==4.1.0
-pytest-xdist==3.3.1
-pytest-mock==3.11.1
-mock==5.2.0
-
-# Pre-commit hooks
-pre-commit==3.3.3
-
-# Type stubs
-types-PyYAML==6.0.12.11
-types-requests==2.31.0.2
-types-setuptools==68.0.0.3
-
-# Documentation
-sphinx==7.1.2
-sphinx-rtd-theme==1.3.0
-myst-parser==2.0.0
-
-# Jupyter for examples
-jupyter==1.0.0
-ipykernel==6.25.0
-
-# Additional development tools
-twine==4.0.2
-build==0.10.0
diff --git a/requirements.txt b/requirements.txt
deleted file mode 100644
index 23116853..00000000
--- a/requirements.txt
+++ /dev/null
@@ -1,20 +0,0 @@
-# Core dependencies
-torch>=1.9.0
-numpy>=1.21.0
-matplotlib>=3.5.0
-seaborn>=0.11.0
-datasets>=2.0.0
-transformers>=4.20.0
-
-# Development dependencies
-pytest>=7.0.0
-pytest-cov>=4.0.0
-black>=22.0.0
-isort>=5.10.0
-flake8>=5.0.0
-mypy>=0.991
-
-# Optional dependencies for benchmarks
-wandb>=0.13.0
-tqdm>=4.64.0
-pandas>=1.5.0
\ No newline at end of file
diff --git a/scripts/format.sh b/scripts/format.sh
index 7d3fdfd2..0fbd9c50 100755
--- a/scripts/format.sh
+++ b/scripts/format.sh
@@ -23,7 +23,7 @@ builtin cd "$ROOT" || exit 1
# Check if development dependencies are installed
check_tool_installed() {
if ! command -v "$1" &> /dev/null; then
- echo "Error: $1 is not installed. Please run: pip install -r requirements-dev.txt"
+ echo "Error: $1 is not installed. Please run: poetry install --with dev"
exit 1
fi
}
@@ -43,7 +43,7 @@ tool_version_check() {
if [[ "$installed_version" != "$required_version" ]]; then
echo "Warning: $tool_name version mismatch. Required: $required_version, Installed: $installed_version"
- echo "Consider running: pip install -r requirements-dev.txt"
+ echo "Consider running: poetry install --with dev"
fi
}
@@ -54,13 +54,13 @@ FLAKE8_VERSION=$(flake8 --version | head -n 1 | awk '{print $1}')
MYPY_VERSION=$(mypy --version | awk '{print $2}')
PYLINT_VERSION=$(pylint --version | head -n 1 | awk '{print $2}')
-# Check versions against requirements-dev.txt
-if [[ -f "requirements-dev.txt" ]]; then
- tool_version_check "black" "$BLACK_VERSION" "$(grep "black==" requirements-dev.txt | cut -d'=' -f3)"
- tool_version_check "isort" "$ISORT_VERSION" "$(grep "isort==" requirements-dev.txt | cut -d'=' -f3)"
- tool_version_check "flake8" "$FLAKE8_VERSION" "$(grep "flake8==" requirements-dev.txt | cut -d'=' -f3)"
- tool_version_check "mypy" "$MYPY_VERSION" "$(grep "mypy==" requirements-dev.txt | cut -d'=' -f3)"
- tool_version_check "pylint" "$PYLINT_VERSION" "$(grep "pylint==" requirements-dev.txt | cut -d'=' -f3)"
+# Check versions against pyproject.toml
+if [[ -f "pyproject.toml" ]]; then
+ tool_version_check "black" "$BLACK_VERSION" "$(grep 'black = ' pyproject.toml | sed 's/.*"\(.*\)".*/\1/')"
+ tool_version_check "isort" "$ISORT_VERSION" "$(grep 'isort = ' pyproject.toml | sed 's/.*"\(.*\)".*/\1/')"
+ tool_version_check "flake8" "$FLAKE8_VERSION" "$(grep 'flake8 = ' pyproject.toml | sed 's/.*"\(.*\)".*/\1/')"
+ tool_version_check "mypy" "$MYPY_VERSION" "$(grep 'mypy = ' pyproject.toml | sed 's/.*"\(.*\)".*/\1/')"
+ tool_version_check "pylint" "$PYLINT_VERSION" "$(grep 'pylint = ' pyproject.toml | sed 's/.*"\(.*\)".*/\1/')"
fi
# Formatting flags
diff --git a/scripts/lint.sh b/scripts/lint.sh
index dba86cdf..0aa9e2af 100755
--- a/scripts/lint.sh
+++ b/scripts/lint.sh
@@ -29,7 +29,7 @@ NC='\033[0m' # No Color
# Check if tool is installed
check_tool() {
if ! command -v "$1" &> /dev/null; then
- echo -e "${RED}Error: $1 is not installed. Please run: pip install -r requirements-dev.txt${NC}"
+ echo -e "${RED}Error: $1 is not installed. Please run: poetry install --with dev${NC}"
return 1
fi
return 0
diff --git a/sparse_attention_hub/metric_logging/logger.py b/sparse_attention_hub/metric_logging/logger.py
index 8663135c..a6788008 100644
--- a/sparse_attention_hub/metric_logging/logger.py
+++ b/sparse_attention_hub/metric_logging/logger.py
@@ -13,23 +13,41 @@
@dataclass
class LogEvent:
- """Log event data structure."""
+ """Log event data structure for metric logging.
+
+ Attributes:
+ timestamp: When the event was logged.
+ metric: Metric identifier string.
+ value: The actual metric value (can be None).
+ metadata: Additional context information like layer, head, etc.
+ location: Auto-inferred location as "module.function" or "class.method".
+ """
timestamp: datetime
- metric: str # Metric identifier string
+ metric: str
value: Union[None, Any]
- metadata: Dict[str, Any] # Additional context (layer, head, etc.)
- location: str # Auto-inferred: "module.function" or "class.method"
+ metadata: Dict[str, Any]
+ location: str
class MicroMetricLogger:
- """Singleton logger for micro metrics with queue-based architecture."""
+ """Singleton logger for micro metrics with queue-based architecture.
+
+ This class provides a singleton pattern for logging micro metrics during
+ sparse attention operations. It supports metric registration, sampling,
+ and configurable limits on record count and flush behavior.
+
+ Attributes:
+ log_path: Optional directory path where log files will be written.
+ flush_every: Number of events after which to flush to disk.
+ flush_interval: Time interval in seconds for automatic flushing.
+ max_records: Maximum number of records to log (None for unlimited).
+ sampling_factor: Probability of logging each event (0.0-1.0).
+ """
_instance: Optional["MicroMetricLogger"] = None
_initialized: bool = False
-
- # Class-level storage for registered metrics (works without initialization)
- _registered_metrics: Dict[str, type] = {} # identifier -> dtype mapping
+ _registered_metrics: Dict[str, type] = {}
def __new__(cls, *args, **kwargs) -> "MicroMetricLogger":
if cls._instance is None:
@@ -39,46 +57,123 @@ def __new__(cls, *args, **kwargs) -> "MicroMetricLogger":
def __init__(
self,
log_path: Optional[str] = None,
- flush_every: int = 1000, # Flush every N events
- flush_interval: float = 60.0, # Flush every N seconds
- enabled_metrics: Union[List[str], str] = None,
- max_records: Optional[int] = None, # Maximum number of events to log
- sampling_factor: float = 1.0, # Probability of logging each event (0.0-1.0)
- ): # List of string identifiers to enable, or "all"
+ flush_every: int = 1000,
+ flush_interval: float = 60.0,
+ enabled_metrics: Union[List[str], str, None] = None,
+ max_records: Optional[int] = None,
+ sampling_factor: float = 1.0,
+ ) -> None:
+ """Initialize the MicroMetricLogger.
+
+ Args:
+ log_path: Optional directory path where log files will be written.
+ flush_every: Number of events after which to flush to disk.
+ flush_interval: Time interval in seconds for automatic flushing.
+ enabled_metrics: List of metric identifiers to enable, "all", or None.
+ max_records: Maximum number of events to log (None for unlimited).
+ sampling_factor: Probability of logging each event (0.0-1.0).
+ """
if not self._initialized:
- self.log_path = log_path
- self.flush_every = flush_every
- self.flush_interval = flush_interval
- self.max_records = max_records
- self.sampling_factor = max(
- 0.0, min(1.0, sampling_factor)
- ) # Clamp to [0.0, 1.0]
-
- # Internal state
- self.log_queue: deque = deque(maxlen=10000) # Circular buffer
- self.enabled_metrics: set = set()
- self.last_flush_time = time.time()
- self._total_records_logged: int = 0 # Track total events logged
-
- # Enable metrics if log_path is provided
- if self.log_path is not None:
- self._ensure_log_directory()
- self.enable_metrics(enabled_metrics)
-
- MicroMetricLogger._initialized = True
+ self._initialize_logger(
+ log_path,
+ flush_every,
+ flush_interval,
+ enabled_metrics,
+ max_records,
+ sampling_factor,
+ )
else:
- if self.log_path and log_path and self.log_path != log_path:
- print(
- f"Warning: MicroMetricLogger already initialized with log_path: {self.log_path}"
- )
+ self._handle_reinitialization(log_path)
+
+ def _initialize_logger(
+ self,
+ log_path: Optional[str],
+ flush_every: int,
+ flush_interval: float,
+ enabled_metrics: Union[List[str], str, None],
+ max_records: Optional[int],
+ sampling_factor: float,
+ ) -> None:
+ """Initialize logger instance for the first time.
+
+ Args:
+ log_path: Directory path for log files.
+ flush_every: Number of events before flushing.
+ flush_interval: Time interval for flushing.
+ enabled_metrics: Metrics to enable initially.
+ max_records: Maximum records limit.
+ sampling_factor: Event sampling probability.
+ """
+ self._set_configuration(
+ log_path, flush_every, flush_interval, max_records, sampling_factor
+ )
+ self._initialize_state()
+ self._configure_logging_if_needed(log_path, enabled_metrics)
+ MicroMetricLogger._initialized = True
+
+ def _set_configuration(
+ self,
+ log_path: Optional[str],
+ flush_every: int,
+ flush_interval: float,
+ max_records: Optional[int],
+ sampling_factor: float,
+ ) -> None:
+ """Set logger configuration parameters.
+
+ Args:
+ log_path: Directory path for log files.
+ flush_every: Number of events before flushing.
+ flush_interval: Time interval for flushing.
+ max_records: Maximum records limit.
+ sampling_factor: Event sampling probability.
+ """
+ self.log_path = log_path
+ self.flush_every = flush_every
+ self.flush_interval = flush_interval
+ self.max_records = max_records
+ self.sampling_factor = max(0.0, min(1.0, sampling_factor))
- # main registration function
+ def _initialize_state(self) -> None:
+ """Initialize internal state variables."""
+ self.log_queue: deque = deque(maxlen=10000)
+ self.enabled_metrics: set = set()
+ self.last_flush_time = time.time()
+ self._total_records_logged: int = 0
+
+ def _configure_logging_if_needed(
+ self, log_path: Optional[str], enabled_metrics: Union[List[str], str, None]
+ ) -> None:
+ """Configure logging if log path is provided.
+
+ Args:
+ log_path: Directory path for log files.
+ enabled_metrics: Metrics to enable initially.
+ """
+ if log_path is not None:
+ self._ensure_log_directory()
+ self.enable_metrics(enabled_metrics)
+
+ def _handle_reinitialization(self, log_path: Optional[str]) -> None:
+ """Handle warning when logger is reinitialized.
+
+ Args:
+ log_path: New log path being requested.
+ """
+ if self.log_path and log_path and self.log_path != log_path:
+ print(
+ f"Warning: MicroMetricLogger already initialized with log_path: {self.log_path}"
+ )
@classmethod
def register_metric(cls, identifier: str, dtype: type) -> None:
"""Register a metric with its string identifier and expected data type.
- This works at class level and doesn't require initialization.
+ This works at class level and doesn't require logger initialization.
+
+ Args:
+ identifier: Unique string identifier for the metric.
+ dtype: Expected data type for the metric values.
"""
if identifier in cls._registered_metrics:
print(f"Warning: Metric '{identifier}' is being re-registered")
@@ -86,10 +181,12 @@ def register_metric(cls, identifier: str, dtype: type) -> None:
@classmethod
def get_registered_metrics(cls) -> Dict[str, type]:
- """Get all registered metrics at class level."""
- return cls._registered_metrics.copy()
+ """Get all registered metrics at class level.
- # helper methods
+ Returns:
+ Dictionary mapping metric identifiers to their expected data types.
+ """
+ return cls._registered_metrics.copy()
def _ensure_log_directory(self) -> None:
"""Ensure the log directory exists."""
@@ -97,24 +194,20 @@ def _ensure_log_directory(self) -> None:
os.makedirs(self.log_path, exist_ok=True)
def _get_calling_location(self) -> str:
- """Get the calling location using inspect module."""
+ """Get the calling location using inspect module.
+
+ Returns:
+ String representation of the calling location in format
+ "module.class.method" or "module.function".
+ """
try:
- # Get the calling frame (skip this method and the log method)
caller_frame = inspect.currentframe().f_back.f_back
if caller_frame is None:
return "unknown"
- # Get module name
- module = inspect.getmodule(caller_frame)
- module_name = module.__name__ if module else "unknown"
-
- # Get function/class name
+ module_name = self._get_module_name(caller_frame)
function_name = caller_frame.f_code.co_name
-
- # Try to get class name if it's a method
- class_name = None
- if "self" in caller_frame.f_locals:
- class_name = caller_frame.f_locals["self"].__class__.__name__
+ class_name = self._get_class_name(caller_frame)
if class_name:
return f"{module_name}.{class_name}.{function_name}"
@@ -123,66 +216,146 @@ def _get_calling_location(self) -> str:
except Exception:
return "unknown"
- def __del__(self):
- """Cleanup when logger is destroyed."""
- self.flush() # Final flush
+ def _get_module_name(self, frame: Any) -> str:
+ """Get module name from frame.
- # api
+ Args:
+ frame: Stack frame object.
- def enable_metrics(self, metrics: Union[List[str], str] = None) -> None:
+ Returns:
+ Module name or "unknown" if not available.
+ """
+ module = inspect.getmodule(frame)
+ return module.__name__ if module else "unknown"
+
+ def _get_class_name(self, frame: Any) -> Optional[str]:
+ """Get class name from frame if it's a method call.
+
+ Args:
+ frame: Stack frame object.
+
+ Returns:
+ Class name if available, None otherwise.
+ """
+ if "self" in frame.f_locals:
+ return frame.f_locals["self"].__class__.__name__
+ return None
+
+ def __del__(self) -> None:
+ """Cleanup when logger is destroyed."""
+ self.flush()
+
+ def enable_metrics(self, metrics: Union[List[str], str, None] = None) -> None:
"""Enable logging for specific metrics.
Args:
- metrics: List of metric identifiers to enable, or "all" for all registered metrics.
- If None, enables no metrics (empty list).
+ metrics: List of metric identifiers to enable, "all" for all registered
+ metrics, or None to disable all metrics.
"""
if metrics == "all":
self.enabled_metrics = set(self._registered_metrics.keys())
elif isinstance(metrics, (list, set)):
- # Only enable metrics that are registered
- valid_metrics = set(metrics) & set(self._registered_metrics.keys())
- invalid_metrics = set(metrics) - set(self._registered_metrics.keys())
- if invalid_metrics:
- print(
- f"Warning: Attempting to enable unregistered metrics: {invalid_metrics}"
- )
- self.enabled_metrics = valid_metrics
+ self._enable_selected_metrics(metrics)
else:
- # Default to empty set
self.enabled_metrics = set()
- def log(self, identifier: str, value: Any, metadata: Dict[str, Any] = None) -> None:
- """Log a metric value with optional metadata. Location is auto-inferred.
+ def _enable_selected_metrics(self, metrics: Union[List[str], set]) -> None:
+ """Enable only the selected metrics that are registered.
- This only works if log_path is defined.
+ Args:
+ metrics: List or set of metric identifiers to enable.
"""
- # Check if logging is configured
- if self.log_path is None:
+ valid_metrics = set(metrics) & set(self._registered_metrics.keys())
+ invalid_metrics = set(metrics) - set(self._registered_metrics.keys())
+
+ if invalid_metrics:
print(
- f"Warning: Cannot log metric '{identifier}' - log_path not defined. Use configure_logging() first."
+ f"Warning: Attempting to enable unregistered metrics: {invalid_metrics}"
)
+
+ self.enabled_metrics = valid_metrics
+
+ def log(
+ self, identifier: str, value: Any, metadata: Optional[Dict[str, Any]] = None
+ ) -> None:
+ """Log a metric value with optional metadata.
+
+ Location is auto-inferred from the calling context. This only works if log_path is defined.
+
+ Args:
+ identifier: Unique string identifier for the metric.
+ value: The metric value to log.
+ metadata: Optional additional context information.
+ """
+ if not self._should_log_metric(identifier):
+ return
+
+ if not self._is_within_limits():
return
- # Check if metric is enabled
+ if not self._passes_sampling():
+ return
+
+ event = self._create_log_event(identifier, value, metadata)
+ self._add_event_and_flush_if_needed(event)
+
+ def _should_log_metric(self, identifier: str) -> bool:
+ """Check if the metric should be logged based on configuration.
+
+ Args:
+ identifier: Metric identifier to check.
+
+ Returns:
+ True if the metric should be logged, False otherwise.
+ """
+ if self.log_path is None:
+ print(
+ f"Warning: Cannot log metric '{identifier}' - log_path not defined. "
+ "Use configure_logging() first."
+ )
+ return False
+
if identifier not in self.enabled_metrics:
print(
f"Warning: Attempting to log metric '{identifier}' which is not enabled"
)
- return
+ return False
- # Check if max_records limit has been reached
- if (
+ return True
+
+ def _is_within_limits(self) -> bool:
+ """Check if logging is within configured limits.
+
+ Returns:
+ True if within limits, False if max records reached.
+ """
+ return not (
self.max_records is not None
and self._total_records_logged >= self.max_records
- ):
- return # Silently ignore - we've hit the limit
+ )
+
+ def _passes_sampling(self) -> bool:
+ """Check if event passes sampling filter.
+
+ Returns:
+ True if event should be logged based on sampling factor.
+ """
+ return self.sampling_factor >= 1.0 or random.random() <= self.sampling_factor
- # Apply sampling factor
- if self.sampling_factor < 1.0 and random.random() > self.sampling_factor:
- return # Event not selected for logging due to sampling
+ def _create_log_event(
+ self, identifier: str, value: Any, metadata: Optional[Dict[str, Any]]
+ ) -> LogEvent:
+ """Create a log event from the provided data.
- # Create log event
- event = LogEvent(
+ Args:
+ identifier: Metric identifier.
+ value: Metric value.
+ metadata: Optional metadata.
+
+ Returns:
+ LogEvent instance ready for logging.
+ """
+ return LogEvent(
timestamp=datetime.now(),
metric=identifier,
value=value,
@@ -190,75 +363,131 @@ def log(self, identifier: str, value: Any, metadata: Dict[str, Any] = None) -> N
location=self._get_calling_location(),
)
- # Add to queue and increment counter
+ def _add_event_and_flush_if_needed(self, event: LogEvent) -> None:
+ """Add event to queue and flush if threshold is reached.
+
+ Args:
+ event: LogEvent to add to the queue.
+ """
self.log_queue.append(event)
self._total_records_logged += 1
- # Check if we should flush
if len(self.log_queue) >= self.flush_every:
self.flush()
def configure_logging(
self,
log_path: str,
- enabled_metrics: Union[List[str], str] = None,
+ enabled_metrics: Union[List[str], str, None] = None,
max_records: Optional[int] = None,
sampling_factor: float = 1.0,
) -> None:
"""Configure logging with a log path and optionally enable metrics.
This must be called before logging can work. Resets the total records counter.
+
+ Args:
+ log_path: Directory path where log files will be written.
+ enabled_metrics: List of metric identifiers to enable, "all", or None.
+ max_records: Maximum number of records to log (None for unlimited).
+ sampling_factor: Probability of logging each event (0.0-1.0).
"""
self.log_path = log_path
self._ensure_log_directory()
self.enable_metrics(enabled_metrics)
+ self._update_logging_limits(max_records, sampling_factor)
+ self._total_records_logged = 0
- # Update limits if provided
+ def _update_logging_limits(
+ self, max_records: Optional[int], sampling_factor: float
+ ) -> None:
+ """Update logging limits and sampling factor.
+
+ Args:
+ max_records: Maximum records limit.
+ sampling_factor: Event sampling probability.
+ """
self.max_records = max_records
self.sampling_factor = max(0.0, min(1.0, sampling_factor))
- # Reset the total records counter when reconfiguring
- self._total_records_logged = 0
-
def flush(self) -> None:
- """Force flush the current queue to disk."""
+ """Force flush the current queue to disk.
+
+ Writes all queued events to the log file in JSONL format.
+ """
if not self.log_queue or self.log_path is None:
return
- # Get current timestamp for filename
- filename = "micro_metrics.jsonl"
- filepath = os.path.join(self.log_path, filename)
+ filepath = os.path.join(self.log_path, "micro_metrics.jsonl")
+ self._write_events_to_file(filepath)
+ self.last_flush_time = time.time()
+
+ def _write_events_to_file(self, filepath: str) -> None:
+ """Write all queued events to the specified file.
- # Write events to file
+ Args:
+ filepath: Path to the log file.
+ """
with open(filepath, "a", encoding="utf-8") as f:
while self.log_queue:
event = self.log_queue.popleft()
- # Convert dataclass to dict and serialize
- event_dict = asdict(event)
- # Convert datetime to ISO format string
- event_dict["timestamp"] = event_dict["timestamp"].isoformat()
+ event_dict = self._serialize_event(event)
f.write(json.dumps(event_dict) + "\n")
- self.last_flush_time = time.time()
+ def _serialize_event(self, event: LogEvent) -> Dict[str, Any]:
+ """Serialize a log event to a dictionary.
+
+ Args:
+ event: LogEvent to serialize.
+
+ Returns:
+ Dictionary representation of the event.
+ """
+ event_dict = asdict(event)
+ event_dict["timestamp"] = event_dict["timestamp"].isoformat()
+ return event_dict
def is_metric_enabled(self, identifier: str) -> bool:
- """Check if a specific metric is requested for logging."""
+ """Check if a specific metric is enabled for logging.
+
+ Args:
+ identifier: Metric identifier to check.
+
+ Returns:
+ True if the metric is enabled, False otherwise.
+ """
return identifier in self.enabled_metrics
def get_enabled_metrics(self) -> set:
- """Get currently enabled metrics."""
+ """Get currently enabled metrics.
+
+ Returns:
+ Set of enabled metric identifiers.
+ """
return self.enabled_metrics.copy()
def is_logging_configured(self) -> bool:
- """Check if logging is configured (log_path is set)."""
+ """Check if logging is configured.
+
+ Returns:
+ True if log_path is set, False otherwise.
+ """
return self.log_path is not None
def get_total_records_logged(self) -> int:
- """Get the total number of records logged since initialization or last configure_logging call."""
+ """Get the total number of records logged.
+
+ Returns:
+ Number of records logged since initialization or last configure_logging call.
+ """
return getattr(self, "_total_records_logged", 0)
def is_max_records_reached(self) -> bool:
- """Check if the maximum number of records has been reached."""
+ """Check if the maximum number of records has been reached.
+
+ Returns:
+ True if max records limit has been reached, False otherwise.
+ """
if self.max_records is None:
return False
return self.get_total_records_logged() >= self.max_records
@@ -274,9 +503,17 @@ def get_records_remaining(self) -> Optional[int]:
return max(0, self.max_records - self.get_total_records_logged())
def get_sampling_factor(self) -> float:
- """Get the current sampling factor."""
+ """Get the current sampling factor.
+
+ Returns:
+ Current sampling factor (0.0-1.0).
+ """
return getattr(self, "sampling_factor", 1.0)
def get_max_records(self) -> Optional[int]:
- """Get the current max_records limit."""
+ """Get the current max_records limit.
+
+ Returns:
+ Maximum records limit, or None if no limit is set.
+ """
return getattr(self, "max_records", None)
diff --git a/sparse_attention_hub/plotting/generator.py b/sparse_attention_hub/plotting/generator.py
index 61b317ca..1847b134 100644
--- a/sparse_attention_hub/plotting/generator.py
+++ b/sparse_attention_hub/plotting/generator.py
@@ -4,19 +4,35 @@
from typing import Any, Dict, Optional
import matplotlib.pyplot as plt
+import numpy as np
import seaborn as sns
from .granularity import Granularity
class PlotGenerator:
- """Generates plots for sparse attention analysis."""
+ """Generates plots for sparse attention analysis.
- def __init__(self, storage_path: str = "./plots"):
+ This class provides functionality to generate various types of plots
+ for analyzing sparse attention patterns at different granularities.
+
+ Attributes:
+ storage_path: Directory path where generated plots will be saved.
+ """
+
+ def __init__(self, storage_path: str = "./plots") -> None:
+ """Initialize the PlotGenerator.
+
+ Args:
+ storage_path: Directory path for storing generated plots.
+ Defaults to "./plots".
+ """
self.storage_path = storage_path
self._ensure_storage_directory()
+ self._setup_plotting_style()
- # Set up plotting style
+ def _setup_plotting_style(self) -> None:
+ """Configure matplotlib and seaborn plotting styles."""
plt.style.use("default")
sns.set_palette("husl")
@@ -34,105 +50,160 @@ def generate_plot(
"""Generate a plot with specified granularity.
Args:
- granularity: Level of granularity for the plot
- data: Data to plot (if None, generates sample data)
- plot_type: Type of plot to generate
- **kwargs: Additional plotting parameters
+ granularity: Level of granularity for the plot (per token, head, or layer).
+ data: Optional data to plot. If None, generates sample data for demonstration.
+ plot_type: Type of plot to generate. Currently unused but reserved for future extensions.
+ **kwargs: Additional plotting parameters passed to the plot generation methods.
Returns:
- Path to the generated plot file
+ Absolute path to the generated plot file.
+
+ Raises:
+ ValueError: If the specified granularity is not supported.
"""
if granularity == Granularity.PER_TOKEN:
- return self._generate_plot_1(granularity, data, **kwargs)
+ return self._generate_line_plot(granularity, data, **kwargs)
elif granularity == Granularity.PER_HEAD:
- return self._generate_plot_2(granularity, data, **kwargs)
+ return self._generate_heatmap_plot(granularity, data, **kwargs)
elif granularity == Granularity.PER_LAYER:
- return self._generate_plot_1(granularity, data, **kwargs)
+ return self._generate_line_plot(granularity, data, **kwargs)
else:
raise ValueError(f"Unsupported granularity: {granularity}")
- def _generate_plot_1(
+ def _generate_line_plot(
self,
granularity: Granularity,
data: Optional[Dict[str, Any]] = None,
**kwargs: Any,
) -> str:
- """Generate plot type 1 (line plots, attention patterns).
+ """Generate line plots for attention patterns.
Args:
- granularity: Level of granularity
- data: Data to plot
- **kwargs: Additional parameters
+ granularity: Level of granularity (per token or per layer).
+ data: Optional data to plot. If None, generates sample data.
+ **kwargs: Additional plotting parameters.
Returns:
- Path to generated plot
+ Path to the generated plot file.
"""
- # TODO: Implement plot generation
fig, ax = plt.subplots(figsize=(10, 6))
if data is None:
- # Generate sample data for demonstration
- import numpy as np
-
- x = np.linspace(0, 10, 100)
- y = np.sin(x) + np.random.normal(0, 0.1, 100)
- ax.plot(x, y, label=f"Sample {granularity.value} data")
+ self._plot_sample_line_data(ax, granularity)
else:
- # Plot actual data
- # Implementation depends on data structure
- pass
+ self._plot_actual_line_data(ax, data)
+
+ self._configure_line_plot_axes(ax, granularity)
+ return self._save_plot(fig, "plot", granularity, data)
+
+ def _plot_sample_line_data(self, ax: plt.Axes, granularity: Granularity) -> None:
+ """Plot sample line data for demonstration.
+
+ Args:
+ ax: Matplotlib axes object to plot on.
+ granularity: Granularity level for labeling.
+ """
+ x = np.linspace(0, 10, 100)
+ y = np.sin(x) + np.random.normal(0, 0.1, 100)
+ ax.plot(x, y, label=f"Sample {granularity.value} data")
+
+ def _plot_actual_line_data(self, ax: plt.Axes, data: Dict[str, Any]) -> None:
+ """Plot actual line data from provided data dictionary.
+
+ Args:
+ ax: Matplotlib axes object to plot on.
+ data: Data dictionary containing plot information.
+ """
+ # Implementation depends on data structure
+ # This is a placeholder for future actual data plotting
+ pass
+
+ def _configure_line_plot_axes(self, ax: plt.Axes, granularity: Granularity) -> None:
+ """Configure axes for line plots.
- ax.set_title(
+ Args:
+ ax: Matplotlib axes object to configure.
+ granularity: Granularity level for title formatting.
+ """
+ title = (
f"Sparse Attention Analysis - {granularity.value.replace('_', ' ').title()}"
)
+ ax.set_title(title)
ax.set_xlabel("Position")
ax.set_ylabel("Attention Weight")
ax.legend()
ax.grid(True, alpha=0.3)
- # Save plot
- filename = f"plot_{granularity.value}_{hash(str(data))}.png"
- filepath = os.path.join(self.storage_path, filename)
- plt.savefig(filepath, dpi=300, bbox_inches="tight")
- plt.close()
-
- return filepath
-
- def _generate_plot_2(
+ def _generate_heatmap_plot(
self,
granularity: Granularity,
data: Optional[Dict[str, Any]] = None,
**kwargs: Any,
) -> str:
- """Generate plot type 2 (heatmaps, attention matrices).
+ """Generate heatmaps for attention matrices.
Args:
- granularity: Level of granularity
- data: Data to plot
- **kwargs: Additional parameters
+ granularity: Level of granularity (typically per head).
+ data: Optional data containing attention matrix. If None, generates sample data.
+ **kwargs: Additional plotting parameters.
Returns:
- Path to generated plot
+ Path to the generated plot file.
+
+ Raises:
+ ValueError: If data is provided but doesn't contain required attention_matrix.
"""
- # TODO: Implement heatmap generation
fig, ax = plt.subplots(figsize=(8, 8))
+ attention_matrix = self._get_attention_matrix(granularity, data)
+ self._create_heatmap(ax, attention_matrix)
+ self._configure_heatmap_axes(ax, granularity)
+
+ return self._save_plot(fig, "heatmap", granularity, data)
+
+ def _get_attention_matrix(
+ self, granularity: Granularity, data: Optional[Dict[str, Any]]
+ ) -> np.ndarray:
+ """Get attention matrix from data or generate sample data.
+
+ Args:
+ granularity: Granularity level for determining matrix size.
+ data: Optional data dictionary containing attention matrix.
+
+ Returns:
+ Attention matrix as numpy array.
+
+ Raises:
+ ValueError: If data is provided but doesn't contain attention_matrix.
+ """
if data is None:
- # Generate sample attention matrix
- import numpy as np
-
- size = 12 if granularity == Granularity.PER_HEAD else 8
- attention_matrix = np.random.rand(size, size)
- attention_matrix = (
- attention_matrix + attention_matrix.T
- ) / 2 # Make symmetric
- else:
- # Use actual data
- attention_matrix = data.get("attention_matrix", None)
- if attention_matrix is None:
- raise ValueError("attention_matrix required in data for heatmap")
+ return self._generate_sample_attention_matrix(granularity)
+
+ attention_matrix = data.get("attention_matrix")
+ if attention_matrix is None:
+ raise ValueError("attention_matrix required in data for heatmap")
+ return attention_matrix
- # Create heatmap
+ def _generate_sample_attention_matrix(self, granularity: Granularity) -> np.ndarray:
+ """Generate sample attention matrix for demonstration.
+
+ Args:
+ granularity: Granularity level for determining matrix size.
+
+ Returns:
+ Symmetric attention matrix.
+ """
+ size = 12 if granularity == Granularity.PER_HEAD else 8
+ attention_matrix = np.random.rand(size, size)
+ return (attention_matrix + attention_matrix.T) / 2 # Make symmetric
+
+ def _create_heatmap(self, ax: plt.Axes, attention_matrix: np.ndarray) -> None:
+ """Create heatmap visualization.
+
+ Args:
+ ax: Matplotlib axes object to plot on.
+ attention_matrix: Attention matrix to visualize.
+ """
sns.heatmap(
attention_matrix,
annot=True,
@@ -142,18 +213,40 @@ def _generate_plot_2(
cbar_kws={"label": "Attention Weight"},
)
- ax.set_title(
- f"Attention Matrix - {granularity.value.replace('_', ' ').title()}"
- )
+ def _configure_heatmap_axes(self, ax: plt.Axes, granularity: Granularity) -> None:
+ """Configure axes for heatmap plots.
+
+ Args:
+ ax: Matplotlib axes object to configure.
+ granularity: Granularity level for title formatting.
+ """
+ title = f"Attention Matrix - {granularity.value.replace('_', ' ').title()}"
+ ax.set_title(title)
ax.set_xlabel("Key Position")
ax.set_ylabel("Query Position")
- # Save plot
- filename = f"heatmap_{granularity.value}_{hash(str(data))}.png"
- filepath = os.path.join(self.storage_path, filename)
- plt.savefig(filepath, dpi=300, bbox_inches="tight")
- plt.close()
+ def _save_plot(
+ self,
+ fig: plt.Figure,
+ plot_type: str,
+ granularity: Granularity,
+ data: Optional[Dict[str, Any]],
+ ) -> str:
+ """Save plot to file and close figure.
+ Args:
+ fig: Matplotlib figure object to save.
+ plot_type: Type of plot for filename.
+ granularity: Granularity level for filename.
+ data: Data used for generating unique filename hash.
+
+ Returns:
+ Absolute path to the saved plot file.
+ """
+ filename = f"{plot_type}_{granularity.value}_{hash(str(data))}.png"
+ filepath = os.path.join(self.storage_path, filename)
+ fig.savefig(filepath, dpi=300, bbox_inches="tight")
+ plt.close(fig)
return filepath
def generate_comparison_plot(
@@ -162,36 +255,50 @@ def generate_comparison_plot(
"""Generate comparison plot for multiple datasets.
Args:
- data_dict: Dictionary mapping labels to data
- granularity: Level of granularity
+ data_dict: Dictionary mapping dataset labels to their corresponding data.
+ granularity: Level of granularity for the comparison plot.
Returns:
- Path to generated comparison plot
+ Path to the generated comparison plot file.
"""
fig, ax = plt.subplots(figsize=(12, 8))
+ self._plot_comparison_data(ax, data_dict)
+ self._configure_comparison_axes(ax, granularity)
+
+ return self._save_plot(fig, "comparison", granularity, data_dict)
+
+ def _plot_comparison_data(self, ax: plt.Axes, data_dict: Dict[str, Any]) -> None:
+ """Plot comparison data for multiple datasets.
+
+ Args:
+ ax: Matplotlib axes object to plot on.
+ data_dict: Dictionary mapping labels to data.
+ """
for label, data in data_dict.items():
- # Plot each dataset
- # Implementation depends on data structure
+ # Plot each dataset - implementation depends on data structure
+ # This is a placeholder for future actual data plotting
pass
- ax.set_title(f"Comparison - {granularity.value.replace('_', ' ').title()}")
+ def _configure_comparison_axes(
+ self, ax: plt.Axes, granularity: Granularity
+ ) -> None:
+ """Configure axes for comparison plots.
+
+ Args:
+ ax: Matplotlib axes object to configure.
+ granularity: Granularity level for title formatting.
+ """
+ title = f"Comparison - {granularity.value.replace('_', ' ').title()}"
+ ax.set_title(title)
ax.legend()
ax.grid(True, alpha=0.3)
- # Save plot
- filename = f"comparison_{granularity.value}_{hash(str(data_dict))}.png"
- filepath = os.path.join(self.storage_path, filename)
- plt.savefig(filepath, dpi=300, bbox_inches="tight")
- plt.close()
-
- return filepath
-
def set_storage_path(self, path: str) -> None:
- """Set the storage path for plots.
+ """Set the storage path for generated plots.
Args:
- path: New storage path
+ path: New directory path where plots will be saved.
"""
self.storage_path = path
self._ensure_storage_directory()
diff --git a/sparse_attention_hub/sparse_attention/efficient_attention/base.py b/sparse_attention_hub/sparse_attention/efficient_attention/base.py
index 0d1b5f01..06be8e79 100644
--- a/sparse_attention_hub/sparse_attention/efficient_attention/base.py
+++ b/sparse_attention_hub/sparse_attention/efficient_attention/base.py
@@ -1,9 +1,8 @@
"""Base classes for efficient attention mechanisms."""
-
from abc import abstractmethod
from dataclasses import dataclass
-from typing import Any, Dict, Optional, Tuple
+from typing import Any, Dict, Optional, Tuple, Type, cast
import torch
from torch import nn
@@ -13,13 +12,22 @@
@dataclass
class EfficientAttentionConfig(SparseAttentionConfig):
- """Configuration class for efficient attention mechanisms."""
+ """Configuration class for efficient attention mechanisms.
+
+ This is a base configuration class for production-ready sparse attention
+ implementations that prioritize efficiency and performance.
+ """
pass
class EfficientAttention(SparseAttention):
- """Abstract base class for efficient attention mechanisms."""
+ """Abstract base class for efficient attention mechanisms.
+
+ This class serves as the base for production-ready sparse attention
+ implementations that are optimized for performance and memory efficiency,
+ such as HashAttention and DoubleSparsity.
+ """
def __init__(self, sparse_attention_config: SparseAttentionConfig) -> None:
"""Initialize efficient attention mechanism.
@@ -54,19 +62,32 @@ def create_from_config(cls, config: SparseAttentionConfig) -> "EfficientAttentio
Args:
config: Configuration for the efficient attention mechanism.
+ Must be an instance of EfficientAttentionConfig.
Returns:
Instance of the efficient attention mechanism.
Raises:
TypeError: If config is not an EfficientAttentionConfig.
+ ValueError: If no implementation is found for the config type.
"""
if not isinstance(config, EfficientAttentionConfig):
raise TypeError(f"Expected EfficientAttentionConfig, got {type(config)}")
- # Import here to avoid circular imports
- from typing import Type, cast
+ registry = cls._get_implementation_registry()
+ concrete_class = cls._get_concrete_class(config, registry)
+ return concrete_class.create_from_config(config)
+ @classmethod
+ def _get_implementation_registry(
+ cls,
+ ) -> Dict[Type[EfficientAttentionConfig], Type["EfficientAttention"]]:
+ """Get the registry mapping config types to implementation classes.
+
+ Returns:
+ Dictionary mapping config types to their corresponding implementation classes.
+ """
+ # Import here to avoid circular imports
from .implementations import (
DoubleSparsity,
DoubleSparsityConfig,
@@ -74,26 +95,32 @@ def create_from_config(cls, config: SparseAttentionConfig) -> "EfficientAttentio
HashAttentionConfig,
)
- # Registry mapping config types to concrete efficient attention classes
- _EFFICIENT_ATTENTION_REGISTRY: Dict[
- Type[EfficientAttentionConfig], Type[EfficientAttention]
- ] = {
+ return {
DoubleSparsityConfig: DoubleSparsity,
HashAttentionConfig: HashAttention,
}
- # Look up the concrete class based on the config type
- concrete_class: Optional[
- Type[EfficientAttention]
- ] = _EFFICIENT_ATTENTION_REGISTRY.get(type(config))
+ @classmethod
+ def _get_concrete_class(
+ cls,
+ config: EfficientAttentionConfig,
+ registry: Dict[Type[EfficientAttentionConfig], Type["EfficientAttention"]],
+ ) -> Type["EfficientAttention"]:
+ """Get the concrete implementation class for the given configuration.
+
+ Args:
+ config: Configuration instance.
+ registry: Registry mapping config types to implementation classes.
+
+ Returns:
+ Concrete implementation class.
+
+ Raises:
+ ValueError: If no implementation is found for the config type.
+ """
+ concrete_class = registry.get(type(config))
if concrete_class is None:
raise ValueError(
f"No efficient attention class found for config type: {type(config)}"
)
-
- # Cast to help mypy understand the type
- concrete_class: Type[EfficientAttention] = cast(
- Type[EfficientAttention], concrete_class
- )
- # Call the concrete class's create_from_config method
- return concrete_class.create_from_config(config)
+ return cast(Type["EfficientAttention"], concrete_class)
diff --git a/sparse_attention_hub/sparse_attention/research_attention/base.py b/sparse_attention_hub/sparse_attention/research_attention/base.py
index 34189068..0fed826a 100644
--- a/sparse_attention_hub/sparse_attention/research_attention/base.py
+++ b/sparse_attention_hub/sparse_attention/research_attention/base.py
@@ -23,13 +23,28 @@
@dataclass
class ResearchAttentionConfig(SparseAttentionConfig):
- """Configuration class for research attention mechanisms."""
+ """Configuration class for research attention mechanisms.
+
+ This configuration specifies the masker components that will be applied
+ sequentially to create sparse attention patterns for research purposes.
+
+ Attributes:
+ masker_configs: List of masker configurations to apply in sequence.
+ """
masker_configs: List[MaskerConfig]
class ResearchAttention(SparseAttention):
- """Base class for research attention mechanisms with maskers."""
+ """Base class for research attention mechanisms with configurable maskers.
+
+ This class implements sparse attention by applying a sequence of maskers
+ to create custom attention patterns. It supports metrics logging and
+ validation of masker configurations.
+
+ Attributes:
+ maskers: List of research maskers to apply sequentially.
+ """
maskers: List[ResearchMasker]
@@ -42,24 +57,33 @@ def __init__(
Args:
sparse_attention_config: Configuration for the sparse attention mechanism.
- maskers: List of research maskers to apply.
+ maskers: List of research maskers to apply in sequence.
Raises:
ValueError: If more than one sampling masker is provided.
"""
super().__init__(sparse_attention_config)
+ self._validate_masker_configuration(maskers)
+ self.maskers = maskers
+
+ def _validate_masker_configuration(self, maskers: List[ResearchMasker]) -> None:
+ """Validate the masker configuration.
+
+ Args:
+ maskers: List of maskers to validate.
- # Validate that there's at most one sampling masker
- sampling_masker_count: int = sum(
+ Raises:
+ ValueError: If more than one sampling masker is provided.
+ """
+ sampling_masker_count = sum(
1 for masker in maskers if isinstance(masker, SamplingMasker)
)
if sampling_masker_count > 1:
raise ValueError(
- "Only one sampling masker supported for efficiency; consider implementing all sampling logic in one masker"
+ "Only one sampling masker supported for efficiency; "
+ "consider implementing all sampling logic in one masker"
)
- self.maskers = maskers
-
def custom_attention(
self,
module: nn.Module,
@@ -75,30 +99,106 @@ def custom_attention(
"""Compute research attention mechanism with masking.
Args:
- module: The attention module
- queries: Query tensor of shape (b, h, sk, d)
- keys: Key tensor of shape (b, h, sq, d)
- values: Value tensor of shape (b, h, sq, d)
- attention_mask: Optional attention mask of shape (b, h, sq, sk)
- scaling: Scaling factor for attention weights
- dropout: Dropout probability
- **kwargs: Additional keyword arguments
+ module: The attention module.
+ queries: Query tensor of shape (b, h, sk, d).
+ keys: Key tensor of shape (b, h, sq, d).
+ values: Value tensor of shape (b, h, sq, d).
+ attention_mask: Optional attention mask of shape (b, h, sq, sk).
+ scaling: Scaling factor for attention weights.
+ dropout: Dropout probability.
+ sparse_meta_data: Additional metadata for sparse attention computation.
+ **kwargs: Additional keyword arguments.
Returns:
Tuple of attention output and optional attention weights.
"""
- # Create an empty Mask object
- mask_shape: Tuple[int, int, int, int] = (
+ sparse_attention_mask = self._create_initial_mask(queries, keys)
+ sparse_attention_mask = self._apply_maskers(
+ sparse_attention_mask=sparse_attention_mask,
+ keys=keys,
+ queries=queries,
+ values=values,
+ attention_mask=attention_mask,
+ scaling=scaling,
+ dropout=dropout,
+ sparse_meta_data=sparse_meta_data,
+ **kwargs,
+ )
+
+ self._log_attention_density(sparse_attention_mask, kwargs)
+
+ attention_output, attention_weights = self._compute_masked_attention(
+ module=module,
+ queries=queries,
+ keys=keys,
+ values=values,
+ attention_mask=attention_mask,
+ scaling=scaling,
+ dropout=dropout,
+ sparse_attention_mask=sparse_attention_mask,
+ **kwargs,
+ )
+
+ self._log_attention_error(
+ module=module,
+ queries=queries,
+ keys=keys,
+ values=values,
+ attention_mask=attention_mask,
+ scaling=scaling,
+ dropout=dropout,
+ attention_output=attention_output,
+ **kwargs,
+ )
+
+ return attention_output, attention_weights
+
+ def _create_initial_mask(self, queries: torch.Tensor, keys: torch.Tensor) -> Mask:
+ """Create an initial empty mask for the attention computation.
+
+ Args:
+ queries: Query tensor.
+ keys: Key tensor.
+
+ Returns:
+ Empty mask with appropriate shape.
+ """
+ mask_shape = (
queries.shape[0],
queries.shape[1],
queries.shape[2],
keys.shape[2],
)
- sparse_attention_mask: Mask = Mask.create_empty_mask(
- mask_shape, dtype=queries.dtype
- )
+ return Mask.create_empty_mask(mask_shape, dtype=queries.dtype)
- # Apply all maskers sequentially, each one on the output of the previous one
+ def _apply_maskers(
+ self,
+ sparse_attention_mask: Mask,
+ keys: torch.Tensor,
+ queries: torch.Tensor,
+ values: torch.Tensor,
+ attention_mask: Optional[torch.Tensor],
+ scaling: float,
+ dropout: float,
+ sparse_meta_data: Dict[Any, Any],
+ **kwargs: Dict[str, Any],
+ ) -> Mask:
+ """Apply all maskers sequentially to create the final sparse attention mask.
+
+ Args:
+ sparse_attention_mask: Initial mask to start with.
+ keys: Key tensor.
+ queries: Query tensor.
+ values: Value tensor.
+ attention_mask: Optional attention mask.
+ scaling: Scaling factor.
+ dropout: Dropout probability.
+ sparse_meta_data: Additional metadata.
+ **kwargs: Additional keyword arguments.
+
+ Returns:
+ Final sparse attention mask after applying all maskers.
+ """
for masker in self.maskers:
sparse_attention_mask = masker.add_mask(
keys=keys,
@@ -111,19 +211,53 @@ def custom_attention(
previous_mask=sparse_attention_mask,
**kwargs,
)
+ return sparse_attention_mask
+ def _log_attention_density(
+ self, sparse_attention_mask: Mask, kwargs: Dict[str, Any]
+ ) -> None:
+ """Log attention density metric if enabled.
+
+ Args:
+ sparse_attention_mask: The sparse attention mask.
+ kwargs: Keyword arguments containing layer information.
+ """
if MicroMetricLogger().is_metric_enabled("research_attention_density"):
MicroMetricLogger().log(
"research_attention_density",
sparse_attention_mask.get_density(),
- metadata={"layer_idx": kwargs["layer_idx"]},
+ metadata={"layer_idx": kwargs.get("layer_idx")},
)
- # Call compute_masked_attention_output on the result of the last mask
- # Always request attention weights to match the expected return signature
- attention_output: torch.Tensor
- attention_weights: torch.Tensor
- attention_output, attention_weights = get_masked_attention_output(
+ def _compute_masked_attention(
+ self,
+ module: nn.Module,
+ queries: torch.Tensor,
+ keys: torch.Tensor,
+ values: torch.Tensor,
+ attention_mask: Optional[torch.Tensor],
+ scaling: float,
+ dropout: float,
+ sparse_attention_mask: Mask,
+ **kwargs: Dict[str, Any],
+ ) -> Tuple[torch.Tensor, torch.Tensor]:
+ """Compute the masked attention output and weights.
+
+ Args:
+ module: The attention module.
+ queries: Query tensor.
+ keys: Key tensor.
+ values: Value tensor.
+ attention_mask: Optional attention mask.
+ scaling: Scaling factor.
+ dropout: Dropout probability.
+ sparse_attention_mask: Sparse attention mask.
+ **kwargs: Additional keyword arguments.
+
+ Returns:
+ Tuple of attention output and attention weights.
+ """
+ return get_masked_attention_output(
module=module,
queries=queries,
keys=keys,
@@ -136,6 +270,31 @@ def custom_attention(
**kwargs,
)
+ def _log_attention_error(
+ self,
+ module: nn.Module,
+ queries: torch.Tensor,
+ keys: torch.Tensor,
+ values: torch.Tensor,
+ attention_mask: Optional[torch.Tensor],
+ scaling: float,
+ dropout: float,
+ attention_output: torch.Tensor,
+ **kwargs: Dict[str, Any],
+ ) -> None:
+ """Log attention output error metric if enabled.
+
+ Args:
+ module: The attention module.
+ queries: Query tensor.
+ keys: Key tensor.
+ values: Value tensor.
+ attention_mask: Optional attention mask.
+ scaling: Scaling factor.
+ dropout: Dropout probability.
+ attention_output: Computed attention output.
+ **kwargs: Additional keyword arguments.
+ """
if MicroMetricLogger().is_metric_enabled("research_attention_output_error"):
true_attention_output, _ = get_true_attention_output(
module,
@@ -153,20 +312,19 @@ def custom_attention(
MicroMetricLogger().log(
"research_attention_output_error",
float(error.item()),
- metadata={"layer_idx": kwargs["layer_idx"]},
+ metadata={"layer_idx": kwargs.get("layer_idx")},
)
- return attention_output, attention_weights
-
@classmethod
def create_from_config(cls, config: SparseAttentionConfig) -> "ResearchAttention":
"""Create research attention instance from configuration.
Args:
config: Configuration for the research attention mechanism.
+ Must be an instance of ResearchAttentionConfig.
Returns:
- Instance of the research attention mechanism.
+ Instance of the research attention mechanism with configured maskers.
Raises:
TypeError: If config is not a ResearchAttentionConfig.
@@ -174,12 +332,23 @@ def create_from_config(cls, config: SparseAttentionConfig) -> "ResearchAttention
if not isinstance(config, ResearchAttentionConfig):
raise TypeError(f"Expected ResearchAttentionConfig, got {type(config)}")
- # Create ResearchMasker objects from the configs using the factory method
- maskers: List[ResearchMasker] = []
- for masker_config in config.masker_configs:
- masker: ResearchMasker = ResearchMasker.create_masker_from_config(
- masker_config
- )
- maskers.append(masker)
-
+ maskers = cls._create_maskers_from_config(config.masker_configs)
return cls(config, maskers)
+
+ @classmethod
+ def _create_maskers_from_config(
+ cls, masker_configs: List[MaskerConfig]
+ ) -> List[ResearchMasker]:
+ """Create research masker objects from their configurations.
+
+ Args:
+ masker_configs: List of masker configurations.
+
+ Returns:
+ List of configured research masker instances.
+ """
+ maskers = []
+ for masker_config in masker_configs:
+ masker = ResearchMasker.create_masker_from_config(masker_config)
+ maskers.append(masker)
+ return maskers
diff --git a/sparse_attention_hub/sparse_attention/research_attention/maskers/sampling/implementations/random_sampling.py b/sparse_attention_hub/sparse_attention/research_attention/maskers/sampling/implementations/random_sampling.py
index ed72b255..a5b06044 100644
--- a/sparse_attention_hub/sparse_attention/research_attention/maskers/sampling/implementations/random_sampling.py
+++ b/sparse_attention_hub/sparse_attention/research_attention/maskers/sampling/implementations/random_sampling.py
@@ -37,7 +37,9 @@ class RandomSamplingMaskerConfig(SamplingMaskerConfig):
no indices are sampled, while 1.0 means all indices are sampled.
"""
- sampling_rate: float # Float in range [0,1] representing fraction of indices to sample
+ sampling_rate: (
+ float # Float in range [0,1] representing fraction of indices to sample
+ )
def __post_init__(self) -> None:
"""Validate sampling_rate after initialization."""
diff --git a/sparse_attention_hub/sparse_attention/utils/hashattention_utils.py b/sparse_attention_hub/sparse_attention/utils/hashattention_utils.py
index ba8b5aea..ad30c215 100644
--- a/sparse_attention_hub/sparse_attention/utils/hashattention_utils.py
+++ b/sparse_attention_hub/sparse_attention/utils/hashattention_utils.py
@@ -1,10 +1,18 @@
+"""Utilities for converting and loading HashAttention weights.
+
+This module provides functions to convert USA (Universal Sparse Attention)
+checkpoint weights to HashAttention format and load them for model use.
+
+Note:
+ Currently only supports 3-layered MLPs. Support for other configurations
+ needs to be implemented.
+"""
+
import pickle
from typing import Dict, List
import torch
-""" This only works for 3 layered MLPs. Need to fix for other cases"""
-
def convert_usa_weights_to_hash_attention(
usa_checkpoint_path: str,
@@ -16,90 +24,24 @@ def convert_usa_weights_to_hash_attention(
"""Convert USA module weights to HashAttentionTopKMasker format.
Args:
- usa_checkpoint_path: Path to USA checkpoint file
- num_layers: Number of layers in the model
- num_heads: Number of attention heads
- num_mlp_layers: Number of MLP layers (default: 3)
- device: Device to load weights on
+ usa_checkpoint_path: Path to the USA checkpoint file.
+ num_layers: Number of transformer layers in the model. Defaults to 32.
+ num_heads: Number of attention heads per layer. Defaults to 32.
+ num_mlp_layers: Number of MLP layers in the hash transformation. Defaults to 3.
+ device: Device to load the weights on ("cpu", "cuda", etc.). Defaults to "cpu".
Returns:
- Dictionary of layer-wise weights in HashAttention format
+ Dictionary mapping layer indices to their corresponding weights in HashAttention format.
+ Each layer contains "query_matrix", "query_bias", "key_matrix", and "key_bias" lists.
"""
-
print(f"Loading USA weights from {usa_checkpoint_path}")
usa_state_dict = torch.load(usa_checkpoint_path, map_location=device)
hat_weights = {}
-
for layer_idx in range(num_layers):
- layer_weights = {
- "query_matrix": [],
- "query_bias": [],
- "key_matrix": [],
- "key_bias": [],
- }
-
- # Collect weights for all heads in this layer
- query_matrices_per_layer = [[] for _ in range(num_mlp_layers)]
- query_biases_per_layer = [[] for _ in range(num_mlp_layers)]
- key_matrices_per_layer = [[] for _ in range(num_mlp_layers)]
- key_biases_per_layer = [[] for _ in range(num_mlp_layers)]
-
- for head_idx in range(num_heads):
- query_prefix = f"{layer_idx}.learning_to_hash_transformation_q.{head_idx}"
- key_prefix = f"{layer_idx}.learning_to_hash_transformation_k.{head_idx}"
-
- # Extract weights from MLP layers (linear layers at indices 0, 2, 4, ...)
- linear_indices = [i * 2 for i in range(num_mlp_layers)]
- for i, linear_idx in enumerate(linear_indices):
- # Query weights
- weight_key = f"{query_prefix}.{linear_idx}.weight"
- bias_key = f"{query_prefix}.{linear_idx}.bias"
-
- if weight_key in usa_state_dict:
- weight = usa_state_dict[
- weight_key
- ].t() # Transpose to (in_features, out_features)
- query_matrices_per_layer[i].append(weight)
-
- if bias_key in usa_state_dict:
- query_biases_per_layer[i].append(usa_state_dict[bias_key])
- else:
- query_biases_per_layer[i].append(
- torch.zeros(usa_state_dict[weight_key].shape[0])
- )
-
- # Key weights
- weight_key = f"{key_prefix}.{linear_idx}.weight"
- bias_key = f"{key_prefix}.{linear_idx}.bias"
-
- if weight_key in usa_state_dict:
- weight = usa_state_dict[
- weight_key
- ].t() # Transpose to (in_features, out_features)
- key_matrices_per_layer[i].append(weight)
-
- if bias_key in usa_state_dict:
- key_biases_per_layer[i].append(usa_state_dict[bias_key])
- else:
- key_biases_per_layer[i].append(
- torch.zeros(usa_state_dict[weight_key].shape[0])
- )
-
- # Stack all heads for each layer
- for i in range(num_mlp_layers):
- if query_matrices_per_layer[i]:
- layer_weights["query_matrix"].append(
- torch.stack(query_matrices_per_layer[i])
- )
- layer_weights["query_bias"].append(
- torch.stack(query_biases_per_layer[i])
- )
- layer_weights["key_matrix"].append(
- torch.stack(key_matrices_per_layer[i])
- )
- layer_weights["key_bias"].append(torch.stack(key_biases_per_layer[i]))
-
+ layer_weights = _convert_layer_weights(
+ usa_state_dict, layer_idx, num_heads, num_mlp_layers
+ )
hat_weights[layer_idx] = layer_weights
print(
@@ -108,6 +50,204 @@ def convert_usa_weights_to_hash_attention(
return hat_weights
+def _convert_layer_weights(
+ usa_state_dict: Dict[str, torch.Tensor],
+ layer_idx: int,
+ num_heads: int,
+ num_mlp_layers: int,
+) -> Dict[str, List[torch.Tensor]]:
+ """Convert weights for a single layer.
+
+ Args:
+ usa_state_dict: USA checkpoint state dictionary.
+ layer_idx: Index of the layer to process.
+ num_heads: Number of attention heads.
+ num_mlp_layers: Number of MLP layers.
+
+ Returns:
+ Dictionary containing converted weights for the layer.
+ """
+ layer_weights = {
+ "query_matrix": [],
+ "query_bias": [],
+ "key_matrix": [],
+ "key_bias": [],
+ }
+
+ # Collect weights for all heads in this layer
+ query_matrices_per_layer = [[] for _ in range(num_mlp_layers)]
+ query_biases_per_layer = [[] for _ in range(num_mlp_layers)]
+ key_matrices_per_layer = [[] for _ in range(num_mlp_layers)]
+ key_biases_per_layer = [[] for _ in range(num_mlp_layers)]
+
+ for head_idx in range(num_heads):
+ _extract_head_weights(
+ usa_state_dict=usa_state_dict,
+ layer_idx=layer_idx,
+ head_idx=head_idx,
+ num_mlp_layers=num_mlp_layers,
+ query_matrices_per_layer=query_matrices_per_layer,
+ query_biases_per_layer=query_biases_per_layer,
+ key_matrices_per_layer=key_matrices_per_layer,
+ key_biases_per_layer=key_biases_per_layer,
+ )
+
+ # Stack all heads for each MLP layer
+ _stack_head_weights(
+ layer_weights=layer_weights,
+ num_mlp_layers=num_mlp_layers,
+ query_matrices_per_layer=query_matrices_per_layer,
+ query_biases_per_layer=query_biases_per_layer,
+ key_matrices_per_layer=key_matrices_per_layer,
+ key_biases_per_layer=key_biases_per_layer,
+ )
+
+ return layer_weights
+
+
+def _extract_head_weights(
+ usa_state_dict: Dict[str, torch.Tensor],
+ layer_idx: int,
+ head_idx: int,
+ num_mlp_layers: int,
+ query_matrices_per_layer: List[List[torch.Tensor]],
+ query_biases_per_layer: List[List[torch.Tensor]],
+ key_matrices_per_layer: List[List[torch.Tensor]],
+ key_biases_per_layer: List[List[torch.Tensor]],
+) -> None:
+ """Extract weights for a single attention head.
+
+ Args:
+ usa_state_dict: USA checkpoint state dictionary.
+ layer_idx: Index of the current layer.
+ head_idx: Index of the current attention head.
+ num_mlp_layers: Number of MLP layers.
+ query_matrices_per_layer: Storage for query weight matrices.
+ query_biases_per_layer: Storage for query bias vectors.
+ key_matrices_per_layer: Storage for key weight matrices.
+ key_biases_per_layer: Storage for key bias vectors.
+ """
+ query_prefix = f"{layer_idx}.learning_to_hash_transformation_q.{head_idx}"
+ key_prefix = f"{layer_idx}.learning_to_hash_transformation_k.{head_idx}"
+
+ # Extract weights from MLP layers (linear layers at indices 0, 2, 4, ...)
+ linear_indices = [i * 2 for i in range(num_mlp_layers)]
+
+ for i, linear_idx in enumerate(linear_indices):
+ _extract_query_weights(
+ usa_state_dict,
+ query_prefix,
+ linear_idx,
+ i,
+ query_matrices_per_layer,
+ query_biases_per_layer,
+ )
+ _extract_key_weights(
+ usa_state_dict,
+ key_prefix,
+ linear_idx,
+ i,
+ key_matrices_per_layer,
+ key_biases_per_layer,
+ )
+
+
+def _extract_query_weights(
+ usa_state_dict: Dict[str, torch.Tensor],
+ query_prefix: str,
+ linear_idx: int,
+ mlp_layer_idx: int,
+ query_matrices_per_layer: List[List[torch.Tensor]],
+ query_biases_per_layer: List[List[torch.Tensor]],
+) -> None:
+ """Extract query weights for a specific MLP layer.
+
+ Args:
+ usa_state_dict: USA checkpoint state dictionary.
+ query_prefix: Prefix for query weight keys.
+ linear_idx: Index of the linear layer.
+ mlp_layer_idx: Index of the MLP layer.
+ query_matrices_per_layer: Storage for query weight matrices.
+ query_biases_per_layer: Storage for query bias vectors.
+ """
+ weight_key = f"{query_prefix}.{linear_idx}.weight"
+ bias_key = f"{query_prefix}.{linear_idx}.bias"
+
+ if weight_key in usa_state_dict:
+ # Transpose to (in_features, out_features)
+ weight = usa_state_dict[weight_key].t()
+ query_matrices_per_layer[mlp_layer_idx].append(weight)
+
+ if bias_key in usa_state_dict:
+ query_biases_per_layer[mlp_layer_idx].append(usa_state_dict[bias_key])
+ else:
+ # Create zero bias if not present
+ zero_bias = torch.zeros(usa_state_dict[weight_key].shape[0])
+ query_biases_per_layer[mlp_layer_idx].append(zero_bias)
+
+
+def _extract_key_weights(
+ usa_state_dict: Dict[str, torch.Tensor],
+ key_prefix: str,
+ linear_idx: int,
+ mlp_layer_idx: int,
+ key_matrices_per_layer: List[List[torch.Tensor]],
+ key_biases_per_layer: List[List[torch.Tensor]],
+) -> None:
+ """Extract key weights for a specific MLP layer.
+
+ Args:
+ usa_state_dict: USA checkpoint state dictionary.
+ key_prefix: Prefix for key weight keys.
+ linear_idx: Index of the linear layer.
+ mlp_layer_idx: Index of the MLP layer.
+ key_matrices_per_layer: Storage for key weight matrices.
+ key_biases_per_layer: Storage for key bias vectors.
+ """
+ weight_key = f"{key_prefix}.{linear_idx}.weight"
+ bias_key = f"{key_prefix}.{linear_idx}.bias"
+
+ if weight_key in usa_state_dict:
+ # Transpose to (in_features, out_features)
+ weight = usa_state_dict[weight_key].t()
+ key_matrices_per_layer[mlp_layer_idx].append(weight)
+
+ if bias_key in usa_state_dict:
+ key_biases_per_layer[mlp_layer_idx].append(usa_state_dict[bias_key])
+ else:
+ # Create zero bias if not present
+ zero_bias = torch.zeros(usa_state_dict[weight_key].shape[0])
+ key_biases_per_layer[mlp_layer_idx].append(zero_bias)
+
+
+def _stack_head_weights(
+ layer_weights: Dict[str, List[torch.Tensor]],
+ num_mlp_layers: int,
+ query_matrices_per_layer: List[List[torch.Tensor]],
+ query_biases_per_layer: List[List[torch.Tensor]],
+ key_matrices_per_layer: List[List[torch.Tensor]],
+ key_biases_per_layer: List[List[torch.Tensor]],
+) -> None:
+ """Stack weights from all heads for each MLP layer.
+
+ Args:
+ layer_weights: Dictionary to store the stacked weights.
+ num_mlp_layers: Number of MLP layers.
+ query_matrices_per_layer: Query weight matrices for all heads.
+ query_biases_per_layer: Query bias vectors for all heads.
+ key_matrices_per_layer: Key weight matrices for all heads.
+ key_biases_per_layer: Key bias vectors for all heads.
+ """
+ for i in range(num_mlp_layers):
+ if query_matrices_per_layer[i]:
+ layer_weights["query_matrix"].append(
+ torch.stack(query_matrices_per_layer[i])
+ )
+ layer_weights["query_bias"].append(torch.stack(query_biases_per_layer[i]))
+ layer_weights["key_matrix"].append(torch.stack(key_matrices_per_layer[i]))
+ layer_weights["key_bias"].append(torch.stack(key_biases_per_layer[i]))
+
+
def create_hat_weights_file_from_usa(
usa_checkpoint_path: str,
target_hat_path: str,
@@ -116,19 +256,21 @@ def create_hat_weights_file_from_usa(
num_mlp_layers: int = 3,
device: str = "cpu",
) -> None:
- """Create HAT weights file from USA checkpoint.
+ """Create HashAttention weights file from USA checkpoint.
+
+ This function converts USA checkpoint weights to HashAttention format
+ and saves them as a pickle file for later use.
Args:
- usa_checkpoint_path: Path to USA checkpoint file
- target_hat_path: Path where HAT weights file will be saved
- num_layers: Number of layers in the model
- num_heads: Number of attention heads
- num_mlp_layers: Number of MLP layers
- device: Device to load weights on
+ usa_checkpoint_path: Path to the input USA checkpoint file.
+ target_hat_path: Path where the HashAttention weights file will be saved.
+ num_layers: Number of transformer layers in the model. Defaults to 32.
+ num_heads: Number of attention heads per layer. Defaults to 32.
+ num_mlp_layers: Number of MLP layers in the hash transformation. Defaults to 3.
+ device: Device to load the weights on ("cpu", "cuda", etc.). Defaults to "cpu".
"""
print("Creating HAT weights file from USA checkpoint...")
- # Convert USA weights to HAT format
hat_weights = convert_usa_weights_to_hash_attention(
usa_checkpoint_path=usa_checkpoint_path,
num_layers=num_layers,
@@ -137,34 +279,70 @@ def create_hat_weights_file_from_usa(
device=device,
)
- # Save to pickle file
- with open(target_hat_path, "wb") as f:
- pickle.dump(hat_weights, f)
-
+ _save_weights_to_file(hat_weights, target_hat_path)
print(f"โ
HAT weights saved to {target_hat_path}")
+def _save_weights_to_file(
+ hat_weights: Dict[int, Dict[str, List[torch.Tensor]]], target_path: str
+) -> None:
+ """Save HashAttention weights to a pickle file.
+
+ Args:
+ hat_weights: Dictionary of HashAttention weights to save.
+ target_path: Path where the weights file will be saved.
+ """
+ with open(target_path, "wb") as f:
+ pickle.dump(hat_weights, f)
+
+
def load_hat_weights(
hat_weights_path: str, device: str = "cpu"
) -> Dict[int, Dict[str, List[torch.Tensor]]]:
- """Load HAT weights from pickle file.
+ """Load HashAttention weights from a pickle file.
+
+ This function loads previously saved HashAttention weights and moves
+ them to the specified device.
Args:
- hat_weights_path: Path to HAT weights pickle file
- device: Device to load weights on
+ hat_weights_path: Path to the HashAttention weights pickle file.
+ device: Device to move the loaded weights to ("cpu", "cuda", etc.). Defaults to "cpu".
Returns:
- Dictionary of layer-wise weights in HashAttention format
+ Dictionary mapping layer indices to their corresponding weights in HashAttention format.
+ Each layer contains "query_matrix", "query_bias", "key_matrix", and "key_bias" lists.
"""
print(f"Loading HAT weights from {hat_weights_path}")
- with open(hat_weights_path, "rb") as f:
- hat_weights = pickle.load(f)
-
- # Move weights to specified device
- for layer_idx, layer_weights in hat_weights.items():
- for key, value in layer_weights.items():
- hat_weights[layer_idx][key] = [tensor.to(device) for tensor in value]
+ hat_weights = _load_weights_from_file(hat_weights_path)
+ _move_weights_to_device(hat_weights, device)
print(f"โ
Loaded HAT weights for {len(hat_weights)} layers")
return hat_weights
+
+
+def _load_weights_from_file(file_path: str) -> Dict[int, Dict[str, List[torch.Tensor]]]:
+ """Load weights from a pickle file.
+
+ Args:
+ file_path: Path to the pickle file.
+
+ Returns:
+ Dictionary of loaded weights.
+ """
+ with open(file_path, "rb") as f:
+ return pickle.load(f)
+
+
+def _move_weights_to_device(
+ hat_weights: Dict[int, Dict[str, List[torch.Tensor]]], device: str
+) -> None:
+ """Move all weights to the specified device.
+
+ Args:
+ hat_weights: Dictionary of HashAttention weights to move.
+ device: Target device for the weights.
+ """
+ for layer_idx, layer_weights in hat_weights.items():
+ for key, tensor_list in layer_weights.items():
+ hat_weights[layer_idx][key] = [tensor.to(device) for tensor in tensor_list]
diff --git a/sparse_attention_hub/sparse_attention/utils/kv_utils.py b/sparse_attention_hub/sparse_attention/utils/kv_utils.py
index afc52d46..81f41def 100644
--- a/sparse_attention_hub/sparse_attention/utils/kv_utils.py
+++ b/sparse_attention_hub/sparse_attention/utils/kv_utils.py
@@ -1,4 +1,5 @@
""" Utility functions for common kv manipulation. """
+
import torch
diff --git a/tests/integration/test_adapter_integration.py b/tests/integration/test_adapter_integration.py
index 0e88542d..b5dc85a4 100644
--- a/tests/integration/test_adapter_integration.py
+++ b/tests/integration/test_adapter_integration.py
@@ -48,14 +48,18 @@ class TestAdapterIntegration:
def setup_method(self) -> None:
"""Set up test fixtures - reset ModelServer singleton."""
from sparse_attention_hub.adapters.model_servers.base import ModelServer
+
ModelServer._instance = None
def teardown_method(self) -> None:
"""Clean up after each test - reset ModelServer singleton."""
from sparse_attention_hub.adapters.model_servers.base import ModelServer
+
ModelServer._instance = None
- @patch("sparse_attention_hub.adapters.model_servers.huggingface.AutoModelForCausalLM")
+ @patch(
+ "sparse_attention_hub.adapters.model_servers.huggingface.AutoModelForCausalLM"
+ )
@patch("sparse_attention_hub.adapters.model_servers.huggingface.AutoTokenizer")
def test_full_request_processing_flow(
self, mock_tokenizer, mock_model, sparse_attention_config
@@ -141,7 +145,9 @@ def test_full_request_processing_flow(
assert mock_tokenizer_instance.encode.call_count == 2
mock_tokenizer_instance.decode.assert_called_once()
- @patch("sparse_attention_hub.adapters.model_servers.huggingface.AutoModelForCausalLM")
+ @patch(
+ "sparse_attention_hub.adapters.model_servers.huggingface.AutoModelForCausalLM"
+ )
@patch("sparse_attention_hub.adapters.model_servers.huggingface.AutoTokenizer")
def test_multiple_questions_processing(
self, mock_tokenizer, mock_model, sparse_attention_config
@@ -246,7 +252,9 @@ def test_multiple_questions_processing(
assert mock_tokenizer_instance.encode.call_count == 3
assert mock_tokenizer_instance.decode.call_count == 2
- @patch("sparse_attention_hub.adapters.model_servers.huggingface.AutoModelForCausalLM")
+ @patch(
+ "sparse_attention_hub.adapters.model_servers.huggingface.AutoModelForCausalLM"
+ )
@patch("sparse_attention_hub.adapters.model_servers.huggingface.AutoTokenizer")
def test_mode_switching_integration(
self, mock_tokenizer, mock_model, sparse_attention_config
@@ -287,7 +295,9 @@ def test_mode_switching_integration(
# Should not raise any errors
pass
- @patch("sparse_attention_hub.adapters.model_servers.huggingface.AutoModelForCausalLM")
+ @patch(
+ "sparse_attention_hub.adapters.model_servers.huggingface.AutoModelForCausalLM"
+ )
@patch("sparse_attention_hub.adapters.model_servers.huggingface.AutoTokenizer")
def test_custom_attention_function_integration(
self, mock_tokenizer, mock_model, sparse_attention_config
@@ -350,7 +360,9 @@ def test_custom_attention_function_integration(
mock_custom_attention.assert_called_once()
assert result is not None
- @patch("sparse_attention_hub.adapters.model_servers.huggingface.AutoModelForCausalLM")
+ @patch(
+ "sparse_attention_hub.adapters.model_servers.huggingface.AutoModelForCausalLM"
+ )
@patch("sparse_attention_hub.adapters.model_servers.huggingface.AutoTokenizer")
def test_error_handling_integration(
self, mock_tokenizer, mock_model, sparse_attention_config
@@ -399,7 +411,9 @@ def test_error_handling_integration(
# missing sparse_meta_data
)
- @patch("sparse_attention_hub.adapters.model_servers.huggingface.AutoModelForCausalLM")
+ @patch(
+ "sparse_attention_hub.adapters.model_servers.huggingface.AutoModelForCausalLM"
+ )
@patch("sparse_attention_hub.adapters.model_servers.huggingface.AutoTokenizer")
def test_adapter_with_device_configuration(
self, mock_tokenizer, mock_model, sparse_attention_config
@@ -453,7 +467,9 @@ def test_adapter_with_device_configuration(
"test-model", device_map="cuda", torch_dtype=torch.float16
)
- @patch("sparse_attention_hub.adapters.model_servers.huggingface.AutoModelForCausalLM")
+ @patch(
+ "sparse_attention_hub.adapters.model_servers.huggingface.AutoModelForCausalLM"
+ )
@patch("sparse_attention_hub.adapters.model_servers.huggingface.AutoTokenizer")
def test_adapter_cleanup_on_exception(
self, mock_tokenizer, mock_model, sparse_attention_config
diff --git a/tests/integration/test_benchmark.py b/tests/integration/test_benchmark.py
index c67b75a3..0c2807d1 100644
--- a/tests/integration/test_benchmark.py
+++ b/tests/integration/test_benchmark.py
@@ -50,7 +50,9 @@ def sparse_attention_config(masker_config):
class TestBenchmarkAdapterIntegration:
"""Test benchmark integration with real adapter interfaces."""
- @patch("sparse_attention_hub.adapters.model_servers.huggingface.AutoModelForCausalLM")
+ @patch(
+ "sparse_attention_hub.adapters.model_servers.huggingface.AutoModelForCausalLM"
+ )
@patch("sparse_attention_hub.adapters.model_servers.huggingface.AutoTokenizer")
def test_benchmark_with_real_adapter_interface(
self,
@@ -118,7 +120,9 @@ def mock_process_request(
assert (result_path / "raw_results.csv").exists()
assert (result_path / "metrics.json").exists()
- @patch("sparse_attention_hub.adapters.model_servers.huggingface.AutoModelForCausalLM")
+ @patch(
+ "sparse_attention_hub.adapters.model_servers.huggingface.AutoModelForCausalLM"
+ )
@patch("sparse_attention_hub.adapters.model_servers.huggingface.AutoTokenizer")
def test_dense_only_adapter_integration(
self, mock_tokenizer_class, mock_model_class, temp_result_dir
@@ -382,6 +386,7 @@ class TestErrorHandlingIntegration:
def test_adapter_failure_recovery(self, temp_result_dir):
"""Test benchmark recovery when adapter fails intermittently."""
+
# Create adapter that fails on certain contexts
def failing_process_request(
request: Request,
diff --git a/tests/unit/adapters/model_servers/test_huggingface.py b/tests/unit/adapters/model_servers/test_huggingface.py
index 5ea48060..256d32c0 100644
--- a/tests/unit/adapters/model_servers/test_huggingface.py
+++ b/tests/unit/adapters/model_servers/test_huggingface.py
@@ -485,8 +485,10 @@ def test_validate_gpu_availability_success(
mock_cuda_available.return_value = True
mock_device_count.return_value = 4
- with patch("torch.cuda.device"), patch("torch.zeros") as mock_zeros, patch(
- "torch.cuda.empty_cache"
+ with (
+ patch("torch.cuda.device"),
+ patch("torch.zeros") as mock_zeros,
+ patch("torch.cuda.empty_cache"),
):
mock_zeros.return_value = Mock()
result = self.server.validate_gpu_availability(0)
diff --git a/tests/unit/adapters/test_adapters.py b/tests/unit/adapters/test_adapters.py
index 60b36b0d..78c07509 100644
--- a/tests/unit/adapters/test_adapters.py
+++ b/tests/unit/adapters/test_adapters.py
@@ -20,6 +20,7 @@
)
from sparse_attention_hub.adapters.model_servers.base import ModelServer
+
@pytest.mark.unit
class TestRequest:
"""Test the Request class."""
@@ -147,12 +148,14 @@ def setup_method(self) -> None:
masker_configs=[self.masker_config]
)
ModelServer._instance = None
-
+
def teardown_method(self) -> None:
"""Clean up after each test."""
ModelServer._instance = None
- @patch("sparse_attention_hub.adapters.model_servers.huggingface.AutoModelForCausalLM")
+ @patch(
+ "sparse_attention_hub.adapters.model_servers.huggingface.AutoModelForCausalLM"
+ )
@patch("sparse_attention_hub.adapters.model_servers.huggingface.AutoTokenizer")
def test_create_model(self, mock_tokenizer, mock_model) -> None:
"""Test model creation."""
@@ -186,7 +189,9 @@ def test_create_model(self, mock_tokenizer, mock_model) -> None:
"test-model", torch_dtype=torch.float16
)
- @patch("sparse_attention_hub.adapters.model_servers.huggingface.AutoModelForCausalLM")
+ @patch(
+ "sparse_attention_hub.adapters.model_servers.huggingface.AutoModelForCausalLM"
+ )
@patch("sparse_attention_hub.adapters.model_servers.huggingface.AutoTokenizer")
def test_create_model_with_torch_dtype(self, mock_tokenizer, mock_model) -> None:
"""Test model creation with torch_dtype parameter."""
@@ -217,7 +222,9 @@ def test_create_model_with_torch_dtype(self, mock_tokenizer, mock_model) -> None
# Check that adapter was created successfully
assert adapter is not None
- @patch("sparse_attention_hub.adapters.model_servers.huggingface.AutoModelForCausalLM")
+ @patch(
+ "sparse_attention_hub.adapters.model_servers.huggingface.AutoModelForCausalLM"
+ )
@patch("sparse_attention_hub.adapters.model_servers.huggingface.AutoTokenizer")
def test_create_model_with_existing_pad_token(
self, mock_tokenizer, mock_model
@@ -243,7 +250,9 @@ def test_create_model_with_existing_pad_token(
# Check that pad_token was not changed
assert adapter.tokenizer.pad_token == ""
- @patch("sparse_attention_hub.adapters.model_servers.huggingface.AutoModelForCausalLM")
+ @patch(
+ "sparse_attention_hub.adapters.model_servers.huggingface.AutoModelForCausalLM"
+ )
@patch("sparse_attention_hub.adapters.model_servers.huggingface.AutoTokenizer")
def test_get_custom_attention_function(self, mock_tokenizer, mock_model) -> None:
"""Test get_custom_attention_function returns a callable."""
@@ -265,7 +274,9 @@ def test_get_custom_attention_function(self, mock_tokenizer, mock_model) -> None
custom_fn = adapter.get_custom_attention_function(adapter.sparse_attention)
assert callable(custom_fn)
- @patch("sparse_attention_hub.adapters.model_servers.huggingface.AutoModelForCausalLM")
+ @patch(
+ "sparse_attention_hub.adapters.model_servers.huggingface.AutoModelForCausalLM"
+ )
@patch("sparse_attention_hub.adapters.model_servers.huggingface.AutoTokenizer")
def test_generate_unique_attention_name(self, mock_tokenizer, mock_model) -> None:
"""Test unique attention name generation."""
@@ -290,7 +301,9 @@ def test_generate_unique_attention_name(self, mock_tokenizer, mock_model) -> Non
assert name2.startswith("sparse_attention_")
assert name1 != name2 # Should be unique
- @patch("sparse_attention_hub.adapters.model_servers.huggingface.AutoModelForCausalLM")
+ @patch(
+ "sparse_attention_hub.adapters.model_servers.huggingface.AutoModelForCausalLM"
+ )
@patch("sparse_attention_hub.adapters.model_servers.huggingface.AutoTokenizer")
def test_enable_sparse_mode_when_not_available(
self, mock_tokenizer, mock_model
@@ -317,7 +330,9 @@ def test_enable_sparse_mode_when_not_available(
exc_info.value
)
- @patch("sparse_attention_hub.adapters.model_servers.huggingface.AutoModelForCausalLM")
+ @patch(
+ "sparse_attention_hub.adapters.model_servers.huggingface.AutoModelForCausalLM"
+ )
@patch("sparse_attention_hub.adapters.model_servers.huggingface.AutoTokenizer")
def test_enable_sparse_and_dense_modes(self, mock_tokenizer, mock_model) -> None:
"""Test enable_sparse_mode and enable_dense_mode context managers."""
@@ -352,7 +367,9 @@ def test_enable_sparse_and_dense_modes(self, mock_tokenizer, mock_model) -> None
assert first_name == second_name
- @patch("sparse_attention_hub.adapters.model_servers.huggingface.AutoModelForCausalLM")
+ @patch(
+ "sparse_attention_hub.adapters.model_servers.huggingface.AutoModelForCausalLM"
+ )
@patch("sparse_attention_hub.adapters.model_servers.huggingface.AutoTokenizer")
def test_inheritance(self, mock_tokenizer, mock_model) -> None:
"""Test that ModelAdapterHF properly inherits from ModelAdapter."""
@@ -372,7 +389,9 @@ def test_inheritance(self, mock_tokenizer, mock_model) -> None:
assert isinstance(adapter, ModelHubAdapterInterface)
assert isinstance(adapter, SparseAttentionAdapterInterface)
- @patch("sparse_attention_hub.adapters.model_servers.huggingface.AutoModelForCausalLM")
+ @patch(
+ "sparse_attention_hub.adapters.model_servers.huggingface.AutoModelForCausalLM"
+ )
@patch("sparse_attention_hub.adapters.model_servers.huggingface.AutoTokenizer")
def test_adapter_properties(self, mock_tokenizer, mock_model) -> None:
"""Test adapter properties are set correctly."""
diff --git a/tests/unit/benchmark/test_longbench.py b/tests/unit/benchmark/test_longbench.py
index 3ff12940..2810ec00 100644
--- a/tests/unit/benchmark/test_longbench.py
+++ b/tests/unit/benchmark/test_longbench.py
@@ -314,7 +314,7 @@ def test_longbench_post_run_evaluate_missing_length_column(self):
"task": ["qasper_e"],
"predicted_answer": ["Answer 1"],
"answers": [["Truth 1"]],
- "all_classes": [[]]
+ "all_classes": [[]],
# Missing 'length' column
}
)
diff --git a/tests/unit/sparse_attention/research_attention/maskers/fixed/implementations/test_hashattention_top_k.py b/tests/unit/sparse_attention/research_attention/maskers/fixed/implementations/test_hashattention_top_k.py
index 680c6090..acc49ed1 100644
--- a/tests/unit/sparse_attention/research_attention/maskers/fixed/implementations/test_hashattention_top_k.py
+++ b/tests/unit/sparse_attention/research_attention/maskers/fixed/implementations/test_hashattention_top_k.py
@@ -5,6 +5,7 @@
:date: 2025-06-29
:summary: Tests for HashAttentionTopKMasker implementation.
"""
+
import os
import pickle
import tempfile
diff --git a/tests/unit/sparse_attention/test_configs_and_factories.py b/tests/unit/sparse_attention/test_configs_and_factories.py
index 095ed934..040730ae 100644
--- a/tests/unit/sparse_attention/test_configs_and_factories.py
+++ b/tests/unit/sparse_attention/test_configs_and_factories.py
@@ -1,4 +1,5 @@
"""Tests for sparse attention config classes and create_from_config methods."""
+
import pytest
diff --git a/tests/unit/sparse_attention/utils/test_mask_attention_utils.py b/tests/unit/sparse_attention/utils/test_mask_attention_utils.py
index 9ef1b93d..13b1756a 100644
--- a/tests/unit/sparse_attention/utils/test_mask_attention_utils.py
+++ b/tests/unit/sparse_attention/utils/test_mask_attention_utils.py
@@ -6,7 +6,6 @@
:summary: Tests for sparse attention. This file is part of the Sparse Attention Hub project.
"""
-
import mock
import numpy as np
import pytest
diff --git a/tutorials/py_examples/02_streaming_llm_demo_hf.py b/tutorials/py_examples/02_streaming_llm_demo_hf.py
index dfcbb084..afa4c7ee 100644
--- a/tutorials/py_examples/02_streaming_llm_demo_hf.py
+++ b/tutorials/py_examples/02_streaming_llm_demo_hf.py
@@ -1,31 +1,35 @@
#!/usr/bin/env python3
-'''
+"""
You can also choose to have more control instead calling process_request directly.
-'''
+"""
import torch
from transformers import AutoTokenizer, AutoModelForCausalLM
import time
-from sparse_attention_hub.sparse_attention.research_attention import ResearchAttentionConfig
+from sparse_attention_hub.sparse_attention.research_attention import (
+ ResearchAttentionConfig,
+)
from sparse_attention_hub.sparse_attention.research_attention.maskers.fixed.implementations import (
- LocalMaskerConfig, SinkMaskerConfig
+ LocalMaskerConfig,
+ SinkMaskerConfig,
)
from sparse_attention_hub.adapters.huggingface import ModelAdapterHF
-device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
+device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model_name = "meta-llama/Llama-3.1-8B-Instruct"
-#D model_name = "microsoft/DialoGPT-medium"
-#D model_name = "google/gemma-3n-E4B-it"
+# D model_name = "microsoft/DialoGPT-medium"
+# D model_name = "google/gemma-3n-E4B-it"
model_name = "microsoft/Phi-4-mini-instruct"
model_name = "mistralai/Mistral-7B-Instruct-v0.3"
model_name = "deepseek-ai/DeepSeek-R1-0528-Qwen3-8B"
import os
-os.chdir('/data/apdesai/code/sparse-attention-hub')
-device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
+os.chdir("/data/apdesai/code/sparse-attention-hub")
+
+device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(f"Device: {device}")
# Create StreamingLLM configuration: Sink (4 tokens) + Local (64 tokens)
sink_config = SinkMaskerConfig(sink_size=4)
@@ -34,10 +38,12 @@
print(f"โ
StreamingLLM config: Sink(4) + Local(64)")
-adapter = ModelAdapterHF(model_name="meta-llama/Llama-3.1-8B-Instruct",
- sparse_attention_config=research_config,
- model_kwargs={"torch_dtype": torch.bfloat16},
- device="cuda:0")
+adapter = ModelAdapterHF(
+ model_name="meta-llama/Llama-3.1-8B-Instruct",
+ sparse_attention_config=research_config,
+ model_kwargs={"torch_dtype": torch.bfloat16},
+ device="cuda:0",
+)
test_content = """
@@ -51,11 +57,13 @@
print("Running Full Attention")
-messages = [
- {"role": "user", "content": test_content}
-]
-test_content = adapter.tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
-input_ids = adapter.tokenizer.encode(test_content, return_tensors="pt", add_special_tokens=False).to(device)
+messages = [{"role": "user", "content": test_content}]
+test_content = adapter.tokenizer.apply_chat_template(
+ messages, tokenize=False, add_generation_prompt=True
+)
+input_ids = adapter.tokenizer.encode(
+ test_content, return_tensors="pt", add_special_tokens=False
+).to(device)
output_ids = adapter.model.generate(input_ids, max_new_tokens=50, do_sample=False)
@@ -66,10 +74,10 @@
with adapter.enable_sparse_mode():
- output_ids = adapter.model.generate(input_ids, max_new_tokens=50, do_sample=False, sparse_meta_data={})
+ output_ids = adapter.model.generate(
+ input_ids, max_new_tokens=50, do_sample=False, sparse_meta_data={}
+ )
# model.generate() validates kwargs and will not let sparse_meta_data to be passed in which is required
# Locally, I removed the validation for this to work. TODO(we can write our own generate function for utility)
output_text = adapter.tokenizer.decode(output_ids[0], skip_special_tokens=True)
print("Streaming Attention Response: ", output_text)
-
-
diff --git a/tutorials/py_examples/02_streaming_llm_tutorial.py b/tutorials/py_examples/02_streaming_llm_tutorial.py
index 3e95ae57..aee56ebf 100644
--- a/tutorials/py_examples/02_streaming_llm_tutorial.py
+++ b/tutorials/py_examples/02_streaming_llm_tutorial.py
@@ -1,17 +1,21 @@
import os
-os.chdir('/data/apdesai/code/sparse-attention-hub')
+
+os.chdir("/data/apdesai/code/sparse-attention-hub")
import torch
from transformers import AutoTokenizer, AutoModelForCausalLM
import time
-from sparse_attention_hub.sparse_attention.research_attention import ResearchAttentionConfig
+from sparse_attention_hub.sparse_attention.research_attention import (
+ ResearchAttentionConfig,
+)
from sparse_attention_hub.sparse_attention.research_attention.maskers.fixed.implementations import (
- LocalMaskerConfig, SinkMaskerConfig
+ LocalMaskerConfig,
+ SinkMaskerConfig,
)
from sparse_attention_hub.adapters.huggingface import ModelAdapterHF
-device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
+device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(f"Device: {device}")
sink_config = SinkMaskerConfig(sink_size=4)
@@ -25,10 +29,12 @@
# model_name = "deepseek-ai/DeepSeek-R1-0528-Qwen3-8B"
model_name = "meta-llama/Llama-3.1-8B-Instruct"
-adapter = ModelAdapterHF(model_name=model_name,
- sparse_attention_config=research_config,
- model_kwargs={"torch_dtype": torch.bfloat16, "device_map": "cuda"},
- device="cuda")
+adapter = ModelAdapterHF(
+ model_name=model_name,
+ sparse_attention_config=research_config,
+ model_kwargs={"torch_dtype": torch.bfloat16, "device_map": "cuda"},
+ device="cuda",
+)
from sparse_attention_hub.adapters import Request
@@ -41,18 +47,16 @@
This approach maintains performance while reducing computational costs for long sequences.
"""
test_questions = [
- "Summarize the above in a single title with less than 10 words. Given only the title.",
- "What are other attention mechanisms that are used in the field of LLMs?"
+ "Summarize the above in a single title with less than 10 words. Given only the title.",
+ "What are other attention mechanisms that are used in the field of LLMs?",
]
request = Request(
context=test_context,
questions=test_questions,
- )
+)
print("Running Streaming Attention on Question")
-response=adapter.process_request(request)
-response_text=response.responses
+response = adapter.process_request(request)
+response_text = response.responses
print("Streaming Attention Response: ", response_text)
-
-
diff --git a/tutorials/py_examples/03_hashattention_demo.py b/tutorials/py_examples/03_hashattention_demo.py
index 33bec796..0985c213 100644
--- a/tutorials/py_examples/03_hashattention_demo.py
+++ b/tutorials/py_examples/03_hashattention_demo.py
@@ -12,96 +12,117 @@
from typing import Dict, List
from transformers import AutoTokenizer, AutoModelForCausalLM
-from sparse_attention_hub.sparse_attention.research_attention import ResearchAttentionConfig
+from sparse_attention_hub.sparse_attention.research_attention import (
+ ResearchAttentionConfig,
+)
from sparse_attention_hub.sparse_attention.research_attention.maskers.fixed.implementations import (
- LocalMaskerConfig, SinkMaskerConfig, HashAttentionTopKMaskerConfig
+ LocalMaskerConfig,
+ SinkMaskerConfig,
+ HashAttentionTopKMaskerConfig,
)
from sparse_attention_hub.adapters import ModelAdapterHF
from sparse_attention_hub.adapters import Request
+
def convert_usa_weights_to_hash_attention(
- usa_checkpoint_path: str,
- num_layers: int = 32,
+ usa_checkpoint_path: str,
+ num_layers: int = 32,
num_heads: int = 32,
- device: str = 'cpu'
+ device: str = "cpu",
) -> Dict[int, Dict[str, List[torch.Tensor]]]:
"""Convert USA module weights to HashAttentionTopKMasker format."""
-
+
print(f"Loading USA weights from {usa_checkpoint_path}")
usa_state_dict = torch.load(usa_checkpoint_path, map_location=device)
-
+
hat_weights = {}
-
+
for layer_idx in range(num_layers):
layer_weights = {
"query_matrix": [],
"query_bias": [],
"key_matrix": [],
- "key_bias": []
+ "key_bias": [],
}
-
+
# Collect weights for all heads in this layer
query_matrices_per_layer = [[] for _ in range(3)] # 3 linear layers
query_biases_per_layer = [[] for _ in range(3)]
key_matrices_per_layer = [[] for _ in range(3)]
key_biases_per_layer = [[] for _ in range(3)]
-
+
for head_idx in range(num_heads):
query_prefix = f"{layer_idx}.learning_to_hash_transformation_q.{head_idx}"
key_prefix = f"{layer_idx}.learning_to_hash_transformation_k.{head_idx}"
-
+
# Extract weights from 3-layer MLP (linear layers at indices 0, 2, 4)
for i, linear_idx in enumerate([0, 2, 4]):
# Query weights
weight_key = f"{query_prefix}.{linear_idx}.weight"
bias_key = f"{query_prefix}.{linear_idx}.bias"
-
+
if weight_key in usa_state_dict:
- weight = usa_state_dict[weight_key].t() # Transpose to (in_features, out_features)
+ weight = usa_state_dict[
+ weight_key
+ ].t() # Transpose to (in_features, out_features)
query_matrices_per_layer[i].append(weight)
-
+
if bias_key in usa_state_dict:
query_biases_per_layer[i].append(usa_state_dict[bias_key])
else:
- query_biases_per_layer[i].append(torch.zeros(usa_state_dict[weight_key].shape[0]))
-
+ query_biases_per_layer[i].append(
+ torch.zeros(usa_state_dict[weight_key].shape[0])
+ )
+
# Key weights
weight_key = f"{key_prefix}.{linear_idx}.weight"
bias_key = f"{key_prefix}.{linear_idx}.bias"
-
+
if weight_key in usa_state_dict:
- weight = usa_state_dict[weight_key].t() # Transpose to (in_features, out_features)
+ weight = usa_state_dict[
+ weight_key
+ ].t() # Transpose to (in_features, out_features)
key_matrices_per_layer[i].append(weight)
-
+
if bias_key in usa_state_dict:
key_biases_per_layer[i].append(usa_state_dict[bias_key])
else:
- key_biases_per_layer[i].append(torch.zeros(usa_state_dict[weight_key].shape[0]))
-
+ key_biases_per_layer[i].append(
+ torch.zeros(usa_state_dict[weight_key].shape[0])
+ )
+
# Stack all heads for each layer
for i in range(3):
if query_matrices_per_layer[i]:
- layer_weights["query_matrix"].append(torch.stack(query_matrices_per_layer[i]))
- layer_weights["query_bias"].append(torch.stack(query_biases_per_layer[i]))
- layer_weights["key_matrix"].append(torch.stack(key_matrices_per_layer[i]))
+ layer_weights["query_matrix"].append(
+ torch.stack(query_matrices_per_layer[i])
+ )
+ layer_weights["query_bias"].append(
+ torch.stack(query_biases_per_layer[i])
+ )
+ layer_weights["key_matrix"].append(
+ torch.stack(key_matrices_per_layer[i])
+ )
layer_weights["key_bias"].append(torch.stack(key_biases_per_layer[i]))
-
+
hat_weights[layer_idx] = layer_weights
-
+
print(f"โ
Converted weights for {num_layers} layers, {num_heads} heads")
return hat_weights
-def create_dummy_weights(num_layers: int = 32, num_heads: int = 32) -> Dict[int, Dict[str, List[torch.Tensor]]]:
+def create_dummy_weights(
+ num_layers: int = 32, num_heads: int = 32
+) -> Dict[int, Dict[str, List[torch.Tensor]]]:
"""Create dummy weights for demonstration purposes."""
-
+
hat_weights = {}
for layer_idx in range(num_layers):
hat_weights[layer_idx] = {
"query_matrix": [
torch.randn(num_heads, 128, 128), # First linear layer
torch.randn(num_heads, 128, 128), # Second linear layer
- torch.randn(num_heads, 128, 32), # Third linear layer
+ torch.randn(num_heads, 128, 32), # Third linear layer
],
"query_bias": [
torch.randn(num_heads, 128),
@@ -124,20 +145,24 @@ def create_dummy_weights(num_layers: int = 32, num_heads: int = 32) -> Dict[int,
def main():
"""Main function demonstrating HashAttention."""
-
+
print("๐ HashAttention Example")
print("=" * 50)
-
+
# Setup
- device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
+ device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(f"Device: {device}")
-
+
# Load weights
- usa_checkpoint_path = "/home/apd10/HashAttention-1.0/artifacts/llama3.1-8b-patch.64K.v1.pt"
-
+ usa_checkpoint_path = (
+ "/home/apd10/HashAttention-1.0/artifacts/llama3.1-8b-patch.64K.v1.pt"
+ )
+
if os.path.exists(usa_checkpoint_path):
try:
- hat_weights = convert_usa_weights_to_hash_attention(usa_checkpoint_path, device=device)
+ hat_weights = convert_usa_weights_to_hash_attention(
+ usa_checkpoint_path, device=device
+ )
print("โ
Successfully loaded USA weights")
except Exception as e:
print(f"โ Error loading USA weights: {e}")
@@ -147,7 +172,7 @@ def main():
print(f"โ USA checkpoint not found at {usa_checkpoint_path}")
print("Creating dummy weights for demonstration...")
hat_weights = create_dummy_weights()
-
+
# Configure HashAttention
local_config = LocalMaskerConfig(window_size=4)
sink_config = SinkMaskerConfig(sink_size=4)
@@ -157,22 +182,24 @@ def main():
hat_mlp_layers=3,
hat_mlp_hidden_size=128,
hat_mlp_activation="silu",
- hat_weights=hat_weights
+ hat_weights=hat_weights,
)
-
+
research_config = ResearchAttentionConfig(
masker_configs=[local_config, sink_config, hash_config]
)
-
+
print("โ
HashAttention config: Local(4) + Sink(4) + Hash(12 bits, 32 heavy)")
-
+
# Load model
model_name = "meta-llama/Llama-3.1-8B-Instruct"
-
- adapter = ModelAdapterHF(model_name=model_name,
- sparse_attention_config=research_config,
- model_kwargs={"torch_dtype": torch.bfloat16, "device_map": "cuda"},
- device="cuda")
+
+ adapter = ModelAdapterHF(
+ model_name=model_name,
+ sparse_attention_config=research_config,
+ model_kwargs={"torch_dtype": torch.bfloat16, "device_map": "cuda"},
+ device="cuda",
+ )
# Prepare test input
test_context = """
The concept of attention mechanisms has revolutionized natural language processing and machine learning.
@@ -182,8 +209,8 @@ def main():
This approach maintains performance while reducing computational costs for long sequences.
"""
test_questions = [
- "Summarize the above in a single title with less than 10 words. Given only the title.",
- "What are other attention mechanisms that are used in the field of LLMs?"
+ "Summarize the above in a single title with less than 10 words. Given only the title.",
+ "What are other attention mechanisms that are used in the field of LLMs?",
]
request = Request(
@@ -192,12 +219,12 @@ def main():
)
print("Running Hash Attention on Question")
- response=adapter.process_request(request, generation_kwargs={"max_new_tokens": 50}, request_kwargs={})
- response_text=response.responses
+ response = adapter.process_request(
+ request, generation_kwargs={"max_new_tokens": 50}, request_kwargs={}
+ )
+ response_text = response.responses
print("Hash Attention Response: ", response_text)
-
-
if __name__ == "__main__":
- main()
\ No newline at end of file
+ main()
diff --git a/tutorials/py_examples/04_simple_benchmark_example.py b/tutorials/py_examples/04_simple_benchmark_example.py
index 30b55c07..c0a2d28c 100644
--- a/tutorials/py_examples/04_simple_benchmark_example.py
+++ b/tutorials/py_examples/04_simple_benchmark_example.py
@@ -22,12 +22,16 @@
# Ensure we're in the correct directory and add to Python path
import sys
-os.chdir('/data/apdesai/code/sparse-attention-hub')
-sys.path.insert(0, '/data/apdesai/code/sparse-attention-hub')
-from sparse_attention_hub.sparse_attention.research_attention import ResearchAttentionConfig
+os.chdir("/data/apdesai/code/sparse-attention-hub")
+sys.path.insert(0, "/data/apdesai/code/sparse-attention-hub")
+
+from sparse_attention_hub.sparse_attention.research_attention import (
+ ResearchAttentionConfig,
+)
from sparse_attention_hub.sparse_attention.research_attention.maskers.fixed.implementations import (
- LocalMaskerConfig, SinkMaskerConfig
+ LocalMaskerConfig,
+ SinkMaskerConfig,
)
from sparse_attention_hub.adapters.huggingface import ModelAdapterHF
from benchmark import MockBenchmark
@@ -35,85 +39,88 @@
def main():
"""Run a simple benchmark comparison between dense and sparse attention."""
-
+
print("๐ฏ Simple Benchmark Example")
print("=" * 40)
-
+
# Configuration
model_name = "microsoft/Phi-4-mini-instruct" # Small model for quick testing
- device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
-
+ device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
+
print(f"Model: {model_name}")
print(f"Device: {device}")
-
+
# Create StreamingLLM configuration: Sink (4 tokens) + Local (32 tokens)
sink_config = SinkMaskerConfig(sink_size=4)
local_config = LocalMaskerConfig(window_size=32)
- streaming_config = ResearchAttentionConfig(masker_configs=[sink_config, local_config])
-
+ streaming_config = ResearchAttentionConfig(
+ masker_configs=[sink_config, local_config]
+ )
+
print("StreamingLLM Config: Sink(4) + Local(32)")
-
+
# Common model arguments
model_kwargs = {
"model_kwargs": {"torch_dtype": torch.bfloat16},
- "device": str(device)
+ "device": str(device),
}
-
+
# Initialize adapters
print("\n๐ง Loading models...")
-
+
# Sparse adapter (StreamingLLM)
print(" โ Loading sparse attention model...")
sparse_adapter = ModelAdapterHF(
- model_name=model_name,
- sparse_attention_config=streaming_config,
- **model_kwargs
+ model_name=model_name, sparse_attention_config=streaming_config, **model_kwargs
)
-
+
# Create mock benchmark - fast and simple
mock_benchmark = MockBenchmark()
-
+
print(f"\n๐ Running MockBenchmark:")
print(f" - 5 simple reading comprehension samples")
print(f" - 3 different contexts (science, history, geography)")
print(f" - Context sharing to test grouping efficiency")
-
+
# Create result directories
result_dir = Path("./simple_benchmark_results")
result_dir.mkdir(exist_ok=True)
-
+
sparse_dir = result_dir / "sparse"
sparse_dir.mkdir(exist_ok=True)
-
+
print("\n๐งช Running Sparse Attention Benchmark...")
start_time = time.time()
-
+
# Show dataset overview
dataset_df = mock_benchmark._load_datasets()
- print(f" Processing {len(dataset_df)} samples across {dataset_df['context'].nunique()} contexts...")
-
+ print(
+ f" Processing {len(dataset_df)} samples across {dataset_df['context'].nunique()} contexts..."
+ )
+
# Run sparse benchmark
with sparse_adapter.enable_sparse_mode():
sparse_metrics = mock_benchmark.run_benchmark(
- adapter=sparse_adapter,
- result_dir=str(sparse_dir)
+ adapter=sparse_adapter, result_dir=str(sparse_dir)
)
-
+
sparse_time = time.time() - start_time
print(f" โ
Sparse completed in {sparse_time:.2f}s")
print(f" Accuracy: {sparse_metrics.get('accuracy', 'N/A')}")
- print(f" Correct predictions: {sparse_metrics.get('correct_predictions', 'N/A')}/{sparse_metrics.get('total_samples', 'N/A')}")
-
+ print(
+ f" Correct predictions: {sparse_metrics.get('correct_predictions', 'N/A')}/{sparse_metrics.get('total_samples', 'N/A')}"
+ )
+
print("\n๐ Results Summary:")
print(f" โข Benchmark: {sparse_metrics['summary']['benchmark']}")
print(f" โข Task: {sparse_metrics['summary']['task']}")
print(f" โข Unique contexts: {sparse_metrics['summary']['unique_contexts']}")
print(f" โข Evaluation method: {sparse_metrics['summary']['evaluation_method']}")
-
+
print(f"\n๐พ Results saved to: {sparse_dir}")
print(" - raw_results.csv: Detailed predictions for each sample")
print(" - metrics.json: Evaluation metrics and summary")
if __name__ == "__main__":
- main()
+ main()
diff --git a/tutorials/py_examples/04_streaming_llm_benchmark_demo.py b/tutorials/py_examples/04_streaming_llm_benchmark_demo.py
index e5dd22cb..ba249b1a 100644
--- a/tutorials/py_examples/04_streaming_llm_benchmark_demo.py
+++ b/tutorials/py_examples/04_streaming_llm_benchmark_demo.py
@@ -32,12 +32,16 @@
# Ensure we're in the correct directory and add to Python path
import sys
-os.chdir('/data/apdesai/code/sparse-attention-hub')
-sys.path.insert(0, '/data/apdesai/code/sparse-attention-hub')
-from sparse_attention_hub.sparse_attention.research_attention import ResearchAttentionConfig
+os.chdir("/data/apdesai/code/sparse-attention-hub")
+sys.path.insert(0, "/data/apdesai/code/sparse-attention-hub")
+
+from sparse_attention_hub.sparse_attention.research_attention import (
+ ResearchAttentionConfig,
+)
from sparse_attention_hub.sparse_attention.research_attention.maskers.fixed.implementations import (
- LocalMaskerConfig, SinkMaskerConfig
+ LocalMaskerConfig,
+ SinkMaskerConfig,
)
from sparse_attention_hub.adapters.huggingface import ModelAdapterHF
from benchmark import Benchmark, LongBench, MockBenchmark
@@ -45,18 +49,18 @@
class BenchmarkRunner:
"""Comprehensive benchmark runner for streaming attention evaluation."""
-
+
def __init__(
- self,
+ self,
model_name: str = "microsoft/Phi-4-mini-instruct",
device: str = "cuda",
sink_size: int = 4,
local_window: int = 64,
result_dir: str = "./benchmark_results",
- benchmark_type: str = "mock"
+ benchmark_type: str = "mock",
):
"""Initialize the benchmark runner.
-
+
Args:
model_name: HuggingFace model identifier
device: Device to run on ('cuda' or 'cpu')
@@ -66,73 +70,73 @@ def __init__(
benchmark_type: Type of benchmark to use ('mock' or 'longbench')
"""
self.model_name = model_name
- self.device = torch.device(device if torch.cuda.is_available() else 'cpu')
+ self.device = torch.device(device if torch.cuda.is_available() else "cpu")
self.sink_size = sink_size
self.local_window = local_window
self.result_dir = Path(result_dir)
self.result_dir.mkdir(exist_ok=True)
self.benchmark_type = benchmark_type
-
+
print(f"๐ Initializing Benchmark Runner")
print(f" Model: {model_name}")
print(f" Device: {self.device}")
print(f" Benchmark: {benchmark_type.upper()}")
print(f" StreamingLLM Config: Sink({sink_size}) + Local({local_window})")
print(f" Results will be saved to: {self.result_dir}")
-
+
# Create StreamingLLM configuration
self.streaming_config = self._create_streaming_config()
-
+
# Initialize adapters
self.dense_adapter = None
self.sparse_adapter = None
-
+
def _create_streaming_config(self) -> ResearchAttentionConfig:
"""Create StreamingLLM attention configuration."""
sink_config = SinkMaskerConfig(sink_size=self.sink_size)
local_config = LocalMaskerConfig(window_size=self.local_window)
return ResearchAttentionConfig(masker_configs=[sink_config, local_config])
-
+
def initialize_adapters(self) -> None:
"""Initialize both dense and sparse attention adapters."""
print("๐ง Initializing model adapters...")
-
+
# Common model arguments
common_kwargs = {
"model_kwargs": {"torch_dtype": torch.bfloat16},
- "device": str(self.device)
+ "device": str(self.device),
}
-
+
# Dense adapter (no sparse attention)
print(" โ Loading dense attention adapter...")
self.dense_adapter = ModelAdapterHF(
model_name=self.model_name,
sparse_attention_config=None, # No sparse attention = dense mode
- **common_kwargs
+ **common_kwargs,
)
-
+
# Sparse adapter (StreamingLLM)
print(" โ Loading sparse attention adapter...")
self.sparse_adapter = ModelAdapterHF(
model_name=self.model_name,
sparse_attention_config=self.streaming_config,
- **common_kwargs
+ **common_kwargs,
)
-
+
print("โ
Adapters initialized successfully!")
-
+
def run_benchmark_comparison(
- self,
+ self,
benchmark_subsets: Optional[List[str]] = None,
- max_samples: Optional[int] = 10
+ max_samples: Optional[int] = 10,
) -> Dict[str, Dict]:
"""Run benchmark comparison between dense and sparse attention.
-
+
Args:
benchmark_subsets: List of benchmark subsets to run. For MockBenchmark, this parameter is ignored.
For LongBench, if None, uses a small default set.
max_samples: Maximum number of samples per subset for quick testing (ignored for MockBenchmark)
-
+
Returns:
Dictionary containing results for both dense and sparse runs
"""
@@ -147,103 +151,109 @@ def run_benchmark_comparison(
if benchmark_subsets is None:
# Use a small subset for demonstration
benchmark_subsets = ["narrativeqa", "qasper", "samsum"]
-
+
print(f"๐งช Running LongBench comparison on subsets: {benchmark_subsets}")
benchmark = LongBench(subsets_to_run=benchmark_subsets)
-
+
results = {}
-
+
# Run dense attention benchmark
print("\n๐ Running Dense Attention Benchmark...")
if self.dense_adapter is None:
- raise RuntimeError("Dense adapter not initialized. Call initialize_adapters() first.")
- results['dense'] = self._run_single_benchmark(
+ raise RuntimeError(
+ "Dense adapter not initialized. Call initialize_adapters() first."
+ )
+ results["dense"] = self._run_single_benchmark(
adapter=self.dense_adapter,
benchmark=benchmark,
mode_name="dense",
- max_samples=max_samples
+ max_samples=max_samples,
)
-
+
# Run sparse attention benchmark
print("\nโก Running Sparse Attention Benchmark...")
if self.sparse_adapter is None:
- raise RuntimeError("Sparse adapter not initialized. Call initialize_adapters() first.")
- results['sparse'] = self._run_single_benchmark(
+ raise RuntimeError(
+ "Sparse adapter not initialized. Call initialize_adapters() first."
+ )
+ results["sparse"] = self._run_single_benchmark(
adapter=self.sparse_adapter,
benchmark=benchmark,
mode_name="sparse",
- max_samples=max_samples
+ max_samples=max_samples,
)
-
+
return results
-
+
def _run_single_benchmark(
- self,
- adapter: ModelAdapterHF,
- benchmark: Benchmark,
+ self,
+ adapter: ModelAdapterHF,
+ benchmark: Benchmark,
mode_name: str,
- max_samples: Optional[int] = None
+ max_samples: Optional[int] = None,
) -> Dict:
"""Run benchmark with a single adapter and track performance metrics.
-
+
Args:
adapter: The model adapter to use
benchmark: The benchmark instance
mode_name: Name for this benchmark run ('dense' or 'sparse')
max_samples: Maximum samples to process (for quick testing)
-
+
Returns:
Dictionary containing benchmark results and performance metrics
"""
# Start memory tracking
tracemalloc.start()
start_time = time.time()
-
+
# Create timestamped result directory
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
mode_result_dir = self.result_dir / f"{mode_name}_{timestamp}"
mode_result_dir.mkdir(exist_ok=True)
-
+
try:
# Load and optionally limit dataset size
print(f" ๐ Loading {mode_name} benchmark data...")
dataset_df = benchmark._load_datasets()
-
+
if max_samples:
- print(f" โ๏ธ Limiting to {max_samples} samples per task for quick testing")
- dataset_df = dataset_df.groupby('task').head(max_samples).reset_index(drop=True)
-
+ print(
+ f" โ๏ธ Limiting to {max_samples} samples per task for quick testing"
+ )
+ dataset_df = (
+ dataset_df.groupby("task").head(max_samples).reset_index(drop=True)
+ )
+
print(f" ๐ Processing {len(dataset_df)} samples...")
-
+
# Run the benchmark
if mode_name == "sparse":
# Use sparse attention mode
with adapter.enable_sparse_mode():
metrics = benchmark.run_benchmark(
- adapter=adapter,
- result_dir=str(mode_result_dir)
+ adapter=adapter, result_dir=str(mode_result_dir)
)
else:
- # Use dense attention mode
+ # Use dense attention mode
metrics = benchmark.run_benchmark(
- adapter=adapter,
- result_dir=str(mode_result_dir)
+ adapter=adapter, result_dir=str(mode_result_dir)
)
-
+
# Track performance metrics
end_time = time.time()
current, peak = tracemalloc.get_traced_memory()
tracemalloc.stop()
-
+
# Compile results
performance_metrics = {
"execution_time_seconds": end_time - start_time,
"peak_memory_mb": peak / 1024 / 1024,
"current_memory_mb": current / 1024 / 1024,
"samples_processed": len(dataset_df),
- "avg_time_per_sample": (end_time - start_time) / len(dataset_df)
+ "avg_time_per_sample": (end_time - start_time) / len(dataset_df),
}
-
+
result = {
"benchmark_metrics": metrics,
"performance_metrics": performance_metrics,
@@ -251,129 +261,157 @@ def _run_single_benchmark(
"model_config": {
"model_name": self.model_name,
"mode": mode_name,
- "streaming_config": {
- "sink_size": self.sink_size,
- "local_window": self.local_window
- } if mode_name == "sparse" else None
- }
+ "streaming_config": (
+ {"sink_size": self.sink_size, "local_window": self.local_window}
+ if mode_name == "sparse"
+ else None
+ ),
+ },
}
-
+
# Save detailed results
with open(mode_result_dir / "detailed_results.json", "w") as f:
json.dump(result, f, indent=2, default=str)
-
+
print(f" โ
{mode_name.title()} benchmark completed!")
print(f" Time: {performance_metrics['execution_time_seconds']:.2f}s")
print(f" Peak Memory: {performance_metrics['peak_memory_mb']:.1f}MB")
print(f" Avg Score: {metrics.get('average_score', 'N/A')}")
-
+
return result
-
+
except Exception as e:
print(f" โ Error in {mode_name} benchmark: {str(e)}")
tracemalloc.stop()
raise e
-
+
def analyze_results(self, results: Dict[str, Dict]) -> None:
"""Analyze and visualize benchmark comparison results.
-
+
Args:
results: Results dictionary from run_benchmark_comparison
"""
print("\n๐ Analyzing Results...")
-
+
# Extract metrics for comparison
- dense_metrics = results['dense']['performance_metrics']
- sparse_metrics = results['sparse']['performance_metrics']
-
- dense_benchmark = results['dense']['benchmark_metrics']
- sparse_benchmark = results['sparse']['benchmark_metrics']
-
+ dense_metrics = results["dense"]["performance_metrics"]
+ sparse_metrics = results["sparse"]["performance_metrics"]
+
+ dense_benchmark = results["dense"]["benchmark_metrics"]
+ sparse_benchmark = results["sparse"]["benchmark_metrics"]
+
# Performance comparison
print("\n๐ Performance Comparison:")
print("โ" * 50)
print(f"{'Metric':<25} {'Dense':<15} {'Sparse':<15} {'Speedup':<10}")
print("โ" * 50)
-
- time_speedup = dense_metrics['execution_time_seconds'] / sparse_metrics['execution_time_seconds']
- memory_reduction = (dense_metrics['peak_memory_mb'] - sparse_metrics['peak_memory_mb']) / dense_metrics['peak_memory_mb'] * 100
-
- print(f"{'Execution Time (s)':<25} {dense_metrics['execution_time_seconds']:<15.2f} {sparse_metrics['execution_time_seconds']:<15.2f} {time_speedup:<10.2f}x")
- print(f"{'Peak Memory (MB)':<25} {dense_metrics['peak_memory_mb']:<15.1f} {sparse_metrics['peak_memory_mb']:<15.1f} {memory_reduction:<10.1f}%")
- print(f"{'Avg Time/Sample (s)':<25} {dense_metrics['avg_time_per_sample']:<15.3f} {sparse_metrics['avg_time_per_sample']:<15.3f}")
-
+
+ time_speedup = (
+ dense_metrics["execution_time_seconds"]
+ / sparse_metrics["execution_time_seconds"]
+ )
+ memory_reduction = (
+ (dense_metrics["peak_memory_mb"] - sparse_metrics["peak_memory_mb"])
+ / dense_metrics["peak_memory_mb"]
+ * 100
+ )
+
+ print(
+ f"{'Execution Time (s)':<25} {dense_metrics['execution_time_seconds']:<15.2f} {sparse_metrics['execution_time_seconds']:<15.2f} {time_speedup:<10.2f}x"
+ )
+ print(
+ f"{'Peak Memory (MB)':<25} {dense_metrics['peak_memory_mb']:<15.1f} {sparse_metrics['peak_memory_mb']:<15.1f} {memory_reduction:<10.1f}%"
+ )
+ print(
+ f"{'Avg Time/Sample (s)':<25} {dense_metrics['avg_time_per_sample']:<15.3f} {sparse_metrics['avg_time_per_sample']:<15.3f}"
+ )
+
# Accuracy comparison
print("\n๐ฏ Accuracy Comparison:")
print("โ" * 40)
- dense_score = dense_benchmark.get('average_score', 0)
- sparse_score = sparse_benchmark.get('average_score', 0)
- accuracy_retention = (sparse_score / dense_score * 100) if dense_score > 0 else 0
-
+ dense_score = dense_benchmark.get("average_score", 0)
+ sparse_score = sparse_benchmark.get("average_score", 0)
+ accuracy_retention = (
+ (sparse_score / dense_score * 100) if dense_score > 0 else 0
+ )
+
print(f"Dense Attention Score: {dense_score:.3f}")
print(f"Sparse Attention Score: {sparse_score:.3f}")
print(f"Accuracy Retention: {accuracy_retention:.1f}%")
-
+
# Create visualization
self._create_visualization(results)
-
+
# Summary
print(f"\n๐ Summary:")
print(f" StreamingLLM achieves {time_speedup:.2f}x speedup")
print(f" Reduces memory usage by {memory_reduction:.1f}%")
print(f" Retains {accuracy_retention:.1f}% of original accuracy")
-
+
def _create_visualization(self, results: Dict[str, Dict]) -> None:
"""Create visualization comparing dense vs sparse performance."""
try:
import matplotlib.pyplot as plt
-
+
fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(12, 10))
- fig.suptitle('Dense vs Sparse Attention Benchmark Comparison', fontsize=16)
-
+ fig.suptitle("Dense vs Sparse Attention Benchmark Comparison", fontsize=16)
+
# Extract data
- dense_perf = results['dense']['performance_metrics']
- sparse_perf = results['sparse']['performance_metrics']
- dense_bench = results['dense']['benchmark_metrics']
- sparse_bench = results['sparse']['benchmark_metrics']
-
+ dense_perf = results["dense"]["performance_metrics"]
+ sparse_perf = results["sparse"]["performance_metrics"]
+ dense_bench = results["dense"]["benchmark_metrics"]
+ sparse_bench = results["sparse"]["benchmark_metrics"]
+
# Execution time comparison
- times = [dense_perf['execution_time_seconds'], sparse_perf['execution_time_seconds']]
- ax1.bar(['Dense', 'Sparse'], times, color=['#ff7f0e', '#2ca02c'])
- ax1.set_ylabel('Execution Time (seconds)')
- ax1.set_title('Execution Time Comparison')
-
+ times = [
+ dense_perf["execution_time_seconds"],
+ sparse_perf["execution_time_seconds"],
+ ]
+ ax1.bar(["Dense", "Sparse"], times, color=["#ff7f0e", "#2ca02c"])
+ ax1.set_ylabel("Execution Time (seconds)")
+ ax1.set_title("Execution Time Comparison")
+
# Memory usage comparison
- memories = [dense_perf['peak_memory_mb'], sparse_perf['peak_memory_mb']]
- ax2.bar(['Dense', 'Sparse'], memories, color=['#ff7f0e', '#2ca02c'])
- ax2.set_ylabel('Peak Memory (MB)')
- ax2.set_title('Memory Usage Comparison')
-
+ memories = [dense_perf["peak_memory_mb"], sparse_perf["peak_memory_mb"]]
+ ax2.bar(["Dense", "Sparse"], memories, color=["#ff7f0e", "#2ca02c"])
+ ax2.set_ylabel("Peak Memory (MB)")
+ ax2.set_title("Memory Usage Comparison")
+
# Accuracy comparison
- scores = [dense_bench.get('average_score', 0), sparse_bench.get('average_score', 0)]
- ax3.bar(['Dense', 'Sparse'], scores, color=['#ff7f0e', '#2ca02c'])
- ax3.set_ylabel('Average Score')
- ax3.set_title('Accuracy Comparison')
+ scores = [
+ dense_bench.get("average_score", 0),
+ sparse_bench.get("average_score", 0),
+ ]
+ ax3.bar(["Dense", "Sparse"], scores, color=["#ff7f0e", "#2ca02c"])
+ ax3.set_ylabel("Average Score")
+ ax3.set_title("Accuracy Comparison")
ax3.set_ylim(0, 1)
-
+
# Per-sample time comparison
- per_sample_times = [dense_perf['avg_time_per_sample'], sparse_perf['avg_time_per_sample']]
- ax4.bar(['Dense', 'Sparse'], per_sample_times, color=['#ff7f0e', '#2ca02c'])
- ax4.set_ylabel('Time per Sample (seconds)')
- ax4.set_title('Per-Sample Processing Time')
-
+ per_sample_times = [
+ dense_perf["avg_time_per_sample"],
+ sparse_perf["avg_time_per_sample"],
+ ]
+ ax4.bar(["Dense", "Sparse"], per_sample_times, color=["#ff7f0e", "#2ca02c"])
+ ax4.set_ylabel("Time per Sample (seconds)")
+ ax4.set_title("Per-Sample Processing Time")
+
plt.tight_layout()
-
+
# Save plot
- plot_path = self.result_dir / f"benchmark_comparison_{datetime.now().strftime('%Y%m%d_%H%M%S')}.png"
- plt.savefig(plot_path, dpi=300, bbox_inches='tight')
+ plot_path = (
+ self.result_dir
+ / f"benchmark_comparison_{datetime.now().strftime('%Y%m%d_%H%M%S')}.png"
+ )
+ plt.savefig(plot_path, dpi=300, bbox_inches="tight")
print(f"๐ Visualization saved to: {plot_path}")
-
+
# Also try to show if in interactive environment
try:
plt.show()
except:
pass
-
+
except ImportError:
print("๐ Matplotlib not available for visualization")
except Exception as e:
@@ -383,31 +421,47 @@ def _create_visualization(self, results: Dict[str, Dict]) -> None:
def main():
"""Main function to run the streaming LLM benchmark demo."""
parser = argparse.ArgumentParser(description="Streaming LLM Benchmark Demo")
- parser.add_argument("--model", default="microsoft/Phi-4-mini-instruct",
- help="HuggingFace model name")
- parser.add_argument("--benchmark", default="mock", choices=["mock", "longbench"],
- help="Benchmark type: 'mock' for quick testing (5 samples) or 'longbench' for comprehensive evaluation")
- parser.add_argument("--subsets", default="narrativeqa,qasper,samsum",
- help="Comma-separated list of LongBench subsets (ignored for MockBenchmark)")
- parser.add_argument("--max-samples", type=int, default=5,
- help="Maximum samples per subset for quick testing (ignored for MockBenchmark)")
- parser.add_argument("--sink-size", type=int, default=4,
- help="Number of sink tokens")
- parser.add_argument("--local-window", type=int, default=64,
- help="Local attention window size")
- parser.add_argument("--device", default="cuda",
- help="Device to use (cuda/cpu)")
- parser.add_argument("--result-dir", default="./benchmark_results",
- help="Directory to save results")
-
+ parser.add_argument(
+ "--model",
+ default="microsoft/Phi-4-mini-instruct",
+ help="HuggingFace model name",
+ )
+ parser.add_argument(
+ "--benchmark",
+ default="mock",
+ choices=["mock", "longbench"],
+ help="Benchmark type: 'mock' for quick testing (5 samples) or 'longbench' for comprehensive evaluation",
+ )
+ parser.add_argument(
+ "--subsets",
+ default="narrativeqa,qasper,samsum",
+ help="Comma-separated list of LongBench subsets (ignored for MockBenchmark)",
+ )
+ parser.add_argument(
+ "--max-samples",
+ type=int,
+ default=5,
+ help="Maximum samples per subset for quick testing (ignored for MockBenchmark)",
+ )
+ parser.add_argument(
+ "--sink-size", type=int, default=4, help="Number of sink tokens"
+ )
+ parser.add_argument(
+ "--local-window", type=int, default=64, help="Local attention window size"
+ )
+ parser.add_argument("--device", default="cuda", help="Device to use (cuda/cpu)")
+ parser.add_argument(
+ "--result-dir", default="./benchmark_results", help="Directory to save results"
+ )
+
args = parser.parse_args()
-
+
print("๐ฏ Streaming LLM Benchmark Demo")
print("=" * 50)
-
+
# Parse subsets
subsets = [s.strip() for s in args.subsets.split(",")]
-
+
# Initialize benchmark runner
runner = BenchmarkRunner(
model_name=args.model,
@@ -415,24 +469,23 @@ def main():
sink_size=args.sink_size,
local_window=args.local_window,
result_dir=args.result_dir,
- benchmark_type=args.benchmark
+ benchmark_type=args.benchmark,
)
-
+
# Initialize adapters
runner.initialize_adapters()
-
+
# Run benchmark comparison
results = runner.run_benchmark_comparison(
- benchmark_subsets=subsets,
- max_samples=args.max_samples
+ benchmark_subsets=subsets, max_samples=args.max_samples
)
-
+
# Analyze results
runner.analyze_results(results)
-
+
print("\nโ
Benchmark demo completed successfully!")
print(f"๐ Results saved to: {runner.result_dir}")
if __name__ == "__main__":
- main()
+ main()
diff --git a/tutorials/py_examples/05_local_sink_oracle_adaptive_demo.py b/tutorials/py_examples/05_local_sink_oracle_adaptive_demo.py
index ba2954ee..f666e62a 100644
--- a/tutorials/py_examples/05_local_sink_oracle_adaptive_demo.py
+++ b/tutorials/py_examples/05_local_sink_oracle_adaptive_demo.py
@@ -23,15 +23,20 @@
# Ensure we're in the correct directory and add to Python path
import sys
-os.chdir('/data/apdesai/code/sparse-attention-hub')
-sys.path.insert(0, '/data/apdesai/code/sparse-attention-hub')
-from sparse_attention_hub.sparse_attention.research_attention import ResearchAttentionConfig
+os.chdir("/data/apdesai/code/sparse-attention-hub")
+sys.path.insert(0, "/data/apdesai/code/sparse-attention-hub")
+
+from sparse_attention_hub.sparse_attention.research_attention import (
+ ResearchAttentionConfig,
+)
from sparse_attention_hub.sparse_attention.research_attention.maskers.fixed.implementations import (
- LocalMaskerConfig, SinkMaskerConfig, OracleTopKConfig
+ LocalMaskerConfig,
+ SinkMaskerConfig,
+ OracleTopKConfig,
)
from sparse_attention_hub.sparse_attention.research_attention.maskers.sampling.implementations import (
- AdaptiveSamplingMaskerConfig
+ AdaptiveSamplingMaskerConfig,
)
from sparse_attention_hub.adapters.huggingface import ModelAdapterHF
from sparse_attention_hub.adapters import Request
@@ -39,79 +44,80 @@
def main():
"""Run a demo with combined maskers: Local + Sink + Oracle-TopK + Adaptive Sampling."""
-
+
print("๐ฏ Local + Sink + Oracle-TopK + Adaptive Sampling Demo")
print("=" * 60)
-
+
# Configuration
model_name = "microsoft/Phi-4-mini-instruct" # Small model for quick testing
- device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
-
+ device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
+
print(f"Model: {model_name}")
print(f"Device: {device}")
-
+
# Create combined masker configuration
print("\n๐ง Creating combined masker configuration...")
-
+
# 1. Local masker: 4-token window for local attention
local_config = LocalMaskerConfig(window_size=4)
print(" โ LocalMasker: window_size=4")
-
+
# 2. Sink masker: 4 sink tokens for global information
sink_config = SinkMaskerConfig(sink_size=4)
print(" โ SinkMasker: sink_size=4")
-
+
# 3. Oracle-TopK masker: 4 tokens based on oracle attention scores
oracle_config = OracleTopKConfig(heavy_size=4)
print(" โ OracleTopKMasker: heavy_size=4")
-
+
# 4. Adaptive sampling masker: 10% base rate with statistical guarantees
adaptive_config = AdaptiveSamplingMaskerConfig(
base_rate_sampling=0.1, # 10% base sampling rate
- epsilon=0.1, # Error bound
- delta=0.05, # Confidence bound
- init_offset=4, # Start from beginning
- local_offset=4 # End at sequence end
+ epsilon=0.1, # Error bound
+ delta=0.05, # Confidence bound
+ init_offset=4, # Start from beginning
+ local_offset=4, # End at sequence end
)
print(" โ AdaptiveSamplingMasker: base_rate=0.1, epsilon=0.1, delta=0.05")
-
+
# Combine all maskers in order of application
combined_config = ResearchAttentionConfig(
- masker_configs=[local_config,
- sink_config,
- oracle_config,
- adaptive_config,
+ masker_configs=[
+ local_config,
+ sink_config,
+ oracle_config,
+ adaptive_config,
]
)
-
+
print("\n๐ Combined Configuration:")
print(" โข Local(4) + Sink(4) + Oracle-TopK(4) + Adaptive(0.1)")
print(" โข Total maskers: 4")
print(" โข Expected sparsity: High (multiple sparse patterns combined)")
-
+
# Common model arguments
model_kwargs = {
"model_kwargs": {"torch_dtype": torch.bfloat16},
- "device": str(device)
+ "device": str(device),
}
-
+
# Initialize adapter
print("\n๐ง Loading model with combined maskers...")
-
+
try:
adapter = ModelAdapterHF(
model_name=model_name,
sparse_attention_config=combined_config,
- **model_kwargs
+ **model_kwargs,
)
print(" โ
Successfully loaded model with combined maskers")
except Exception as e:
print(f" โ Error loading model: {e}")
return
-
+
# Prepare test input
print("\n๐ Preparing test input...")
-
+
test_context = """
The sparse attention mechanism combines multiple attention patterns to achieve
both computational efficiency and performance. This approach uses:
@@ -124,50 +130,55 @@ def main():
This combination allows the model to maintain high performance while significantly
reducing computational complexity for long sequences.
"""
-
+
test_questions = [
"What are the four attention patterns used in this sparse attention mechanism?",
"How does adaptive sampling contribute to the overall efficiency?",
- "Explain the difference between local and sink attention patterns."
+ "Explain the difference between local and sink attention patterns.",
]
-
+
request = Request(
context=test_context,
questions=test_questions,
)
-
+
print(f" โ Context length: {len(test_context.split())} words")
print(f" โ Number of questions: {len(test_questions)}")
-
+
# Run inference
print("\n๐งช Running inference with combined maskers...")
start_time = time.time()
-
+
try:
- response = adapter.process_request(request, generation_kwargs={"max_new_tokens": 100}, request_kwargs={"max_context": 1024})
+ response = adapter.process_request(
+ request,
+ generation_kwargs={"max_new_tokens": 100},
+ request_kwargs={"max_context": 1024},
+ )
response_text = response.responses
-
+
inference_time = time.time() - start_time
print(f" โ
Inference completed in {inference_time:.2f}s")
-
+
# Display results
print("\n๐ Results:")
print("-" * 40)
-
+
for i, (question, answer) in enumerate(zip(test_questions, response_text), 1):
print(f"\nQ{i}: {question}")
print(f"A{i}: {answer}")
-
+
except Exception as e:
print(f" โ Error during inference: {e}")
import traceback
+
traceback.print_exc()
return
-
+
# Performance analysis
print("\n๐ Performance Analysis:")
print("-" * 40)
-
+
# Calculate expected sparsity
# This is a rough estimate based on the masker configurations
print(" โข Local attention: ~25% sparsity (4-token window)")
@@ -175,18 +186,20 @@ def main():
print(" โข Oracle-TopK: ~25% sparsity (4 top tokens)")
print(" โข Adaptive sampling: ~10% base rate + adaptive budget")
print(" โข Combined effect: High sparsity with maintained performance")
-
+
print("\n๐ก Key Benefits:")
print(" โข Local attention captures immediate context")
print(" โข Sink attention preserves global information")
print(" โข Oracle attention selects most relevant tokens")
print(" โข Adaptive sampling provides statistical guarantees")
print(" โข Combined approach balances efficiency and performance")
-
+
print(f"\nโ
Demo completed successfully!")
print(" The combined masker approach demonstrates how different attention")
- print(" patterns can be layered to create sophisticated sparse attention mechanisms.")
+ print(
+ " patterns can be layered to create sophisticated sparse attention mechanisms."
+ )
if __name__ == "__main__":
- main()
+ main()