diff --git a/.github/workflows/python-publish.yaml b/.github/workflows/python-publish.yaml
index 986aca23..759b7c6a 100644
--- a/.github/workflows/python-publish.yaml
+++ b/.github/workflows/python-publish.yaml
@@ -14,18 +14,14 @@ jobs:
steps:
- uses: actions/checkout@v4
- - name: Set up Python
- uses: actions/setup-python@v5
+ - name: Install uv
+ uses: astral-sh/setup-uv@v4
with:
- python-version: "3.x"
- - name: Install pypa/build
- run: >-
- python3 -m
- pip install
- build
- --user
+ version: "latest"
+ - name: Set up Python
+ run: uv python install 3.12
- name: Build a binary wheel and a source tarball
- run: python3 -m build
+ run: uv build
- name: Store the distribution packages
uses: actions/upload-artifact@v4
with:
diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml
new file mode 100644
index 00000000..37bdc41b
--- /dev/null
+++ b/.github/workflows/tests.yaml
@@ -0,0 +1,59 @@
+name: Tests
+
+on:
+ push:
+ branches:
+ - main
+ pull_request:
+ branches:
+ - main
+
+jobs:
+ test:
+ name: Test Python ${{ matrix.python-version }}
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ python-version: ["3.10", "3.11", "3.12"]
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Install uv
+ uses: astral-sh/setup-uv@v4
+ with:
+ version: "latest"
+
+ - name: Set up Python ${{ matrix.python-version }}
+ run: uv python install ${{ matrix.python-version }}
+
+ - name: Install dependencies
+ run: uv sync
+
+ - name: Run tests
+ run: uv run pytest opt/test/ -v --tb=short
+
+ lint:
+ name: Lint
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Install uv
+ uses: astral-sh/setup-uv@v4
+ with:
+ version: "latest"
+
+ - name: Set up Python
+ run: uv python install 3.12
+
+ - name: Install dependencies
+ run: uv sync
+
+ - name: Run ruff check
+ run: uv run ruff check opt/
+
+ - name: Run ruff format check
+ run: uv run ruff format --check opt/
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 30f90a15..0534eb06 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -2,17 +2,15 @@
# See https://pre-commit.com/hooks.html for more hooks
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
- rev: "v4.5.0"
+ rev: "v6.0.0"
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-added-large-files
- repo: https://github.com/astral-sh/ruff-pre-commit
- rev: "v0.3.4"
+ rev: "v0.14.10"
hooks:
- id: ruff
- language_version: python3.12
- types_or: [ python, pyi, jupyter ]
+ types_or: [python, pyi, jupyter]
- id: ruff-format
- language_version: python3.12
diff --git a/README.md b/README.md
index 0d502ea7..c5f2559a 100644
--- a/README.md
+++ b/README.md
@@ -6,11 +6,12 @@ Useful Optimizer is a dedicated set of optimization algorithms for numeric probl
## Version
-The current version of Useful Optimizer is 0.1.0.
+The current version of Useful Optimizer is 0.1.2.
## Features
-- A wide range of optimization algorithms.
+- A wide range of optimization algorithms (54+ implementations).
+- Organized into logical categories for easy discovery.
- Easy to use and integrate.
- Suitable for various numeric problems.
- Having fun to play with the algorithms
@@ -23,12 +24,18 @@ To install Useful Optimizer, you can use pip:
pip install git+https://github.com/Anselmoo/useful-optimizer
```
+Or using uv (recommended):
+
+```bash
+uv add git+https://github.com/Anselmoo/useful-optimizer
+```
+
## Usage
Here's a basic example of how to use Useful Optimizer:
```python
-from opt.benchmark import CrossEntropyMethod
+from opt.metaheuristic import CrossEntropyMethod
from opt.benchmark.functions import shifted_ackley
optimizer = CrossEntropyMethod(
@@ -44,12 +51,12 @@ print(f"Best solution: {best_solution}")
print(f"Best fitness: {best_fitness}")
```
-You can also use the new gradient-based optimizers:
+You can also use the gradient-based optimizers:
```python
-from opt.stochastic_gradient_descent import SGD
-from opt.adamw import AdamW
-from opt.bfgs import BFGS
+from opt.gradient_based import SGD, AdamW
+from opt.classical import BFGS
+from opt.benchmark.functions import shifted_ackley
# Gradient-based optimization
sgd = SGD(func=shifted_ackley, lower_bound=-12.768, upper_bound=12.768, dim=2, learning_rate=0.01)
@@ -64,6 +71,36 @@ bfgs = BFGS(func=shifted_ackley, lower_bound=-12.768, upper_bound=12.768, dim=2,
best_solution, best_fitness = bfgs.search()
```
+### Import Styles
+
+All optimizers can be imported in two ways:
+
+```python
+# Categorical imports (recommended for discoverability)
+from opt.swarm_intelligence import ParticleSwarm
+from opt.gradient_based import AdamW
+from opt.classical import BFGS
+
+# Direct imports from root (backward compatible)
+from opt import ParticleSwarm, AdamW, BFGS
+```
+
+## Project Structure
+
+Optimizers are organized into categorical subfolders:
+
+```
+opt/
+├── gradient_based/ # 11 gradient-based optimizers
+├── swarm_intelligence/ # 12 swarm-based optimizers
+├── evolutionary/ # 6 evolutionary algorithms
+├── classical/ # 9 classical methods
+├── metaheuristic/ # 12 metaheuristic algorithms
+├── constrained/ # 2 constrained optimization methods
+├── probabilistic/ # 2 probabilistic optimizers
+└── benchmark/ # Benchmark functions
+```
+
## Implemented Optimizers
The current version of Useful Optimizer includes 54 optimization algorithms, each implemented as a separate module. Each optimizer is linked to its corresponding source code for easy reference and study.
@@ -73,108 +110,100 @@ The current version of Useful Optimizer includes 54 optimization algorithms, eac
These optimizers use gradient information to guide the search process and are commonly used in machine learning and deep learning applications.
-- **[Adadelta](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/adadelta.py)** - An adaptive learning rate method that uses only first-order information
-- **[Adagrad](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/adagrad.py)** - Adapts the learning rate to the parameters, performing smaller updates for frequently occurring features
-- **[Adaptive Moment Estimation (Adam)](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/adaptive_moment_estimation.py)** - Combines advantages of AdaGrad and RMSProp with bias correction
-- **[AdaMax](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/adamax.py)** - Adam variant using infinity norm for second moment estimation
-- **[AdamW](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/adamw.py)** - Adam with decoupled weight decay for better regularization
-- **[AMSGrad](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/amsgrad.py)** - Adam variant with non-decreasing second moment estimates
-- **[BFGS](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/bfgs.py)** - Quasi-Newton method approximating the inverse Hessian matrix
-- **[Conjugate Gradient](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/conjugate_gradient.py)** - Efficient iterative method for solving systems of linear equations
-- **[L-BFGS](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/lbfgs.py)** - Limited-memory version of BFGS for large-scale optimization
-- **[Nadam](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/nadam.py)** - Nesterov-accelerated Adam combining Adam with Nesterov momentum
-- **[Nesterov Accelerated Gradient](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/nesterov_accelerated_gradient.py)** - Accelerated gradient method with lookahead momentum
-- **[RMSprop](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/rmsprop.py)** - Adaptive learning rate using moving average of squared gradients
-- **[SGD with Momentum](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/sgd_momentum.py)** - SGD enhanced with momentum for faster convergence
-- **[Stochastic Gradient Descent](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/stochastic_gradient_descent.py)** - Fundamental gradient-based optimization algorithm
+- **[Adadelta](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/gradient_based/adadelta.py)** - An adaptive learning rate method that uses only first-order information
+- **[Adagrad](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/gradient_based/adagrad.py)** - Adapts the learning rate to the parameters, performing smaller updates for frequently occurring features
+- **[Adaptive Moment Estimation (Adam)](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/gradient_based/adaptive_moment_estimation.py)** - Combines advantages of AdaGrad and RMSProp with bias correction
+- **[AdaMax](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/gradient_based/adamax.py)** - Adam variant using infinity norm for second moment estimation
+- **[AdamW](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/gradient_based/adamw.py)** - Adam with decoupled weight decay for better regularization
+- **[AMSGrad](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/gradient_based/amsgrad.py)** - Adam variant with non-decreasing second moment estimates
+- **[Nadam](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/gradient_based/nadam.py)** - Nesterov-accelerated Adam combining Adam with Nesterov momentum
+- **[Nesterov Accelerated Gradient](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/gradient_based/nesterov_accelerated_gradient.py)** - Accelerated gradient method with lookahead momentum
+- **[RMSprop](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/gradient_based/rmsprop.py)** - Adaptive learning rate using moving average of squared gradients
+- **[SGD with Momentum](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/gradient_based/sgd_momentum.py)** - SGD enhanced with momentum for faster convergence
+- **[Stochastic Gradient Descent](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/gradient_based/stochastic_gradient_descent.py)** - Fundamental gradient-based optimization algorithm
-🦋 Nature-Inspired Metaheuristics
-
-These algorithms are inspired by natural phenomena and biological behaviors to solve optimization problems.
-
-- **[Ant Colony Optimization](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/ant_colony.py)** - Mimics ant behavior for finding optimal paths
-- **[Artificial Fish Swarm Algorithm](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/artificial_fish_swarm_algorithm.py)** - Simulates fish behavior for global optimization
-- **[Bat Algorithm](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/bat_algorithm.py)** - Inspired by echolocation behavior of microbats
-- **[Bee Algorithm](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/bee_algorithm.py)** - Based on honey bee food foraging behavior
-- **[Cat Swarm Optimization](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/cat_swarm_optimization.py)** - Models cat behavior with seeking and tracing modes
-- **[Cuckoo Search](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/cuckoo_search.py)** - Based on obligate brood parasitism of cuckoo species
-- **[Eagle Strategy](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/eagle_strategy.py)** - Inspired by hunting behavior of eagles
-- **[Firefly Algorithm](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/firefly_algorithm.py)** - Based on flashing behavior of fireflies
-- **[Glowworm Swarm Optimization](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/glowworm_swarm_optimization.py)** - Inspired by glowworm behavior
-- **[Grey Wolf Optimizer](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/grey_wolf_optimizer.py)** - Mimics leadership hierarchy and hunting of grey wolves
-- **[Particle Swarm Optimization](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/particle_swarm.py)** - Simulates social behavior of bird flocking or fish schooling
-- **[Shuffled Frog Leaping Algorithm](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/shuffled_frog_leaping_algorithm.py)** - Inspired by memetic evolution of frogs searching for food
-- **[Squirrel Search Algorithm](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/squirrel_search.py)** - Based on caching behavior of squirrels
-- **[Whale Optimization Algorithm](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/whale_optimization_algorithm.py)** - Simulates social behavior of humpback whales
+🦋 Swarm Intelligence Algorithms
+
+These algorithms are inspired by collective behavior of decentralized, self-organized systems.
+
+- **[Ant Colony Optimization](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/swarm_intelligence/ant_colony.py)** - Mimics ant behavior for finding optimal paths
+- **[Artificial Fish Swarm Algorithm](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/swarm_intelligence/artificial_fish_swarm_algorithm.py)** - Simulates fish behavior for global optimization
+- **[Bat Algorithm](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/swarm_intelligence/bat_algorithm.py)** - Inspired by echolocation behavior of microbats
+- **[Bee Algorithm](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/swarm_intelligence/bee_algorithm.py)** - Based on honey bee food foraging behavior
+- **[Cat Swarm Optimization](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/swarm_intelligence/cat_swarm_optimization.py)** - Models cat behavior with seeking and tracing modes
+- **[Cuckoo Search](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/swarm_intelligence/cuckoo_search.py)** - Based on obligate brood parasitism of cuckoo species
+- **[Firefly Algorithm](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/swarm_intelligence/firefly_algorithm.py)** - Based on flashing behavior of fireflies
+- **[Glowworm Swarm Optimization](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/swarm_intelligence/glowworm_swarm_optimization.py)** - Inspired by glowworm behavior
+- **[Grey Wolf Optimizer](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/swarm_intelligence/grey_wolf_optimizer.py)** - Mimics leadership hierarchy and hunting of grey wolves
+- **[Particle Swarm Optimization](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/swarm_intelligence/particle_swarm.py)** - Simulates social behavior of bird flocking or fish schooling
+- **[Squirrel Search Algorithm](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/swarm_intelligence/squirrel_search.py)** - Based on caching behavior of squirrels
+- **[Whale Optimization Algorithm](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/swarm_intelligence/whale_optimization_algorithm.py)** - Simulates social behavior of humpback whales
-🧬 Evolutionary and Population-Based Algorithms
+🧬 Evolutionary Algorithms
These algorithms use principles of evolution and population dynamics to find optimal solutions.
-- **[CMA-ES](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/cma_es.py)** - Covariance Matrix Adaptation Evolution Strategy for continuous optimization
-- **[Cultural Algorithm](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/cultural_algorithm.py)** - Evolutionary algorithm based on cultural evolution
-- **[Differential Evolution](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/differential_evolution.py)** - Population-based algorithm using biological evolution mechanisms
-- **[Estimation of Distribution Algorithm](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/estimation_of_distribution_algorithm.py)** - Uses probabilistic model of candidate solutions
-- **[Genetic Algorithm](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/genetic_algorithm.py)** - Inspired by Charles Darwin's theory of natural evolution
-- **[Imperialist Competitive Algorithm](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/imperialist_competitive_algorithm.py)** - Based on imperialistic competition
-
-
-
-
-🎯 Local Search and Classical Methods
-
-Traditional optimization methods include local search techniques and classical mathematical approaches.
-
-- **[Hill Climbing](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/hill_climbing.py)** - Local search algorithm that continuously moves toward increasing value
-- **[Nelder-Mead](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/nelder_mead.py)** - Derivative-free simplex method for optimization
-- **[Powell's Method](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/powell.py)** - Derivative-free optimization using conjugate directions
-- **[Simulated Annealing](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/simulated_annealing.py)** - Probabilistic technique mimicking the annealing process in metallurgy
-- **[Tabu Search](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/tabu_search.py)** - Metaheuristic using memory structures to avoid cycles
-- **[Variable Depth Search](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/variable_depth_search.py)** - Explores search space with variable-depth first search
-- **[Variable Neighbourhood Search](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/variable_neighbourhood_search.py)** - Metaheuristic for discrete optimization problems
-- **[Very Large Scale Neighborhood Search](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/very_large_scale_neighborhood_search.py)** - Explores very large neighborhoods efficiently
+- **[CMA-ES](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/evolutionary/cma_es.py)** - Covariance Matrix Adaptation Evolution Strategy for continuous optimization
+- **[Cultural Algorithm](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/evolutionary/cultural_algorithm.py)** - Evolutionary algorithm based on cultural evolution
+- **[Differential Evolution](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/evolutionary/differential_evolution.py)** - Population-based algorithm using biological evolution mechanisms
+- **[Estimation of Distribution Algorithm](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/evolutionary/estimation_of_distribution_algorithm.py)** - Uses probabilistic model of candidate solutions
+- **[Genetic Algorithm](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/evolutionary/genetic_algorithm.py)** - Inspired by Charles Darwin's theory of natural evolution
+- **[Imperialist Competitive Algorithm](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/evolutionary/imperialist_competitive_algorithm.py)** - Based on imperialistic competition
-🔬 Physics and Mathematical-Inspired Algorithms
+🎯 Classical Optimization Methods
-Algorithms inspired by physical phenomena and mathematical concepts.
+Traditional optimization methods including local search techniques and classical mathematical approaches.
-- **[Colliding Bodies Optimization](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/colliding_bodies_optimization.py)** - Physics-inspired method based on collision and explosion
-- **[Harmony Search](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/harmony_search.py)** - Music-inspired metaheuristic optimization
-- **[Sine Cosine Algorithm](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/sine_cosine_algorithm.py)** - Based on mathematical sine and cosine functions
-- **[Stochastic Diffusion Search](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/stochastic_diffusion_search.py)** - Population-based search inspired by diffusion processes
-- **[Stochastic Fractal Search](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/stochastic_fractal_search.py)** - Inspired by fractal shapes and Brownian motion
+- **[BFGS](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/classical/bfgs.py)** - Quasi-Newton method approximating the inverse Hessian matrix
+- **[Conjugate Gradient](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/classical/conjugate_gradient.py)** - Efficient iterative method for solving systems of linear equations
+- **[Hill Climbing](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/classical/hill_climbing.py)** - Local search algorithm that continuously moves toward increasing value
+- **[L-BFGS](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/classical/lbfgs.py)** - Limited-memory version of BFGS for large-scale optimization
+- **[Nelder-Mead](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/classical/nelder_mead.py)** - Derivative-free simplex method for optimization
+- **[Powell's Method](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/classical/powell.py)** - Derivative-free optimization using conjugate directions
+- **[Simulated Annealing](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/classical/simulated_annealing.py)** - Probabilistic technique mimicking the annealing process in metallurgy
+- **[Tabu Search](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/classical/tabu_search.py)** - Metaheuristic using memory structures to avoid cycles
+- **[Trust Region](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/classical/trust_region.py)** - Robust optimization method using trusted model regions
-📊 Statistical and Probabilistic Methods
-
-Methods based on statistical inference and probabilistic approaches.
-
-- **[Cross Entropy Method](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/cross_entropy_method.py)** - Monte Carlo method for importance sampling and optimization
-- **[Particle Filter](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/particle_filter.py)** - Statistical filter for nonlinear state estimation
-- **[Parzen Tree Estimator](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/parzen_tree_stimator.py)** - Non-parametric density estimation method
+🔬 Metaheuristic Algorithms
+
+High-level problem-independent algorithmic frameworks for exploring search spaces.
+
+- **[Colliding Bodies Optimization](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/metaheuristic/colliding_bodies_optimization.py)** - Physics-inspired method based on collision and explosion
+- **[Cross Entropy Method](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/metaheuristic/cross_entropy_method.py)** - Monte Carlo method for importance sampling and optimization
+- **[Eagle Strategy](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/metaheuristic/eagle_strategy.py)** - Inspired by hunting behavior of eagles
+- **[Harmony Search](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/metaheuristic/harmony_search.py)** - Music-inspired metaheuristic optimization
+- **[Particle Filter](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/metaheuristic/particle_filter.py)** - Statistical filter for nonlinear state estimation
+- **[Shuffled Frog Leaping Algorithm](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/metaheuristic/shuffled_frog_leaping_algorithm.py)** - Inspired by memetic evolution of frogs searching for food
+- **[Sine Cosine Algorithm](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/metaheuristic/sine_cosine_algorithm.py)** - Based on mathematical sine and cosine functions
+- **[Stochastic Diffusion Search](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/metaheuristic/stochastic_diffusion_search.py)** - Population-based search inspired by diffusion processes
+- **[Stochastic Fractal Search](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/metaheuristic/stochastic_fractal_search.py)** - Inspired by fractal shapes and Brownian motion
+- **[Variable Depth Search](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/metaheuristic/variable_depth_search.py)** - Explores search space with variable-depth first search
+- **[Variable Neighbourhood Search](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/metaheuristic/variable_neighbourhood_search.py)** - Metaheuristic for discrete optimization problems
+- **[Very Large Scale Neighborhood Search](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/metaheuristic/very_large_scale_neighborhood_search.py)** - Explores very large neighborhoods efficiently
-🔧 Specialized and Constrained Optimization
+🔧 Constrained & Probabilistic Optimization
-Specialized algorithms for particular types of optimization problems.
+Specialized algorithms for constrained problems and probabilistic approaches.
-- **[Augmented Lagrangian Method](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/augmented_lagrangian_method.py)** - Method for solving constrained optimization problems
-- **[Linear Discriminant Analysis](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/linear_discriminant_analysis.py)** - Statistical method for dimensionality reduction and classification
-- **[Successive Linear Programming](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/successive_linear_programming.py)** - Method for nonlinear optimization using linear approximations
-- **[Trust Region](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/trust_region.py)** - Robust optimization method using trusted model regions
+- **[Augmented Lagrangian Method](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/constrained/augmented_lagrangian_method.py)** - Method for solving constrained optimization problems
+- **[Successive Linear Programming](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/constrained/successive_linear_programming.py)** - Method for nonlinear optimization using linear approximations
+- **[Linear Discriminant Analysis](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/probabilistic/linear_discriminant_analysis.py)** - Statistical method for dimensionality reduction and classification
+- **[Parzen Tree Estimator](https://github.com/Anselmoo/useful-optimizer/blob/main/opt/probabilistic/parzen_tree_stimator.py)** - Non-parametric density estimation method (TPE)
diff --git a/opt/__init__.py b/opt/__init__.py
index 8f419e6a..93ec51fc 100644
--- a/opt/__init__.py
+++ b/opt/__init__.py
@@ -1,6 +1,155 @@
-"""Useful optimizers, a set of optimization algorithms."""
+"""Useful optimizers, a set of optimization algorithms.
+
+This package provides 54 optimization algorithms organized into categories:
+- gradient_based: Gradient-based optimizers (AdaDelta, AdaGrad, Adam, etc.)
+- swarm_intelligence: Nature-inspired swarm algorithms (PSO, ACO, etc.)
+- evolutionary: Evolutionary algorithms (GA, DE, CMA-ES, etc.)
+- classical: Classical optimization methods (BFGS, Nelder-Mead, etc.)
+- metaheuristic: Metaheuristic algorithms (Harmony Search, etc.)
+- constrained: Constrained optimization methods
+- probabilistic: Probabilistic optimization methods
+
+All optimizers are re-exported at the package level for backward compatibility.
+"""
from __future__ import annotations
+# Base class
+from opt.abstract_optimizer import AbstractOptimizer
+
+# Classical algorithms
+from opt.classical import BFGS
+from opt.classical import LBFGS
+from opt.classical import ConjugateGradient
+from opt.classical import HillClimbing
+from opt.classical import NelderMead
+from opt.classical import Powell
+from opt.classical import SimulatedAnnealing
+from opt.classical import TabuSearch
+from opt.classical import TrustRegion
+
+# Constrained optimization
+from opt.constrained import AugmentedLagrangian
+from opt.constrained import SuccessiveLinearProgramming
+
+# Evolutionary algorithms
+from opt.evolutionary import CMAESAlgorithm
+from opt.evolutionary import CulturalAlgorithm
+from opt.evolutionary import DifferentialEvolution
+from opt.evolutionary import EstimationOfDistributionAlgorithm
+from opt.evolutionary import GeneticAlgorithm
+from opt.evolutionary import ImperialistCompetitiveAlgorithm
+from opt.gradient_based import SGD
+
+# Gradient-based algorithms
+from opt.gradient_based import ADAGrad
+from opt.gradient_based import ADAMOptimization
+from opt.gradient_based import AMSGrad
+from opt.gradient_based import AdaDelta
+from opt.gradient_based import AdaMax
+from opt.gradient_based import AdamW
+from opt.gradient_based import Nadam
+from opt.gradient_based import NesterovAcceleratedGradient
+from opt.gradient_based import RMSprop
+from opt.gradient_based import SGDMomentum
+
+# Metaheuristic algorithms
+from opt.metaheuristic import CollidingBodiesOptimization
+from opt.metaheuristic import CrossEntropyMethod
+from opt.metaheuristic import EagleStrategy
+from opt.metaheuristic import HarmonySearch
+from opt.metaheuristic import ParticleFilter
+from opt.metaheuristic import ShuffledFrogLeapingAlgorithm
+from opt.metaheuristic import SineCosineAlgorithm
+from opt.metaheuristic import StochasticDiffusionSearch
+from opt.metaheuristic import StochasticFractalSearch
+from opt.metaheuristic import VariableDepthSearch
+from opt.metaheuristic import VariableNeighborhoodSearch
+from opt.metaheuristic import VeryLargeScaleNeighborhood
+
+# Probabilistic algorithms
+from opt.probabilistic import LDAnalysis
+from opt.probabilistic import ParzenTreeEstimator
+
+# Swarm intelligence algorithms
+from opt.swarm_intelligence import AntColony
+from opt.swarm_intelligence import ArtificialFishSwarm
+from opt.swarm_intelligence import BatAlgorithm
+from opt.swarm_intelligence import BeeAlgorithm
+from opt.swarm_intelligence import CatSwarmOptimization
+from opt.swarm_intelligence import CuckooSearch
+from opt.swarm_intelligence import FireflyAlgorithm
+from opt.swarm_intelligence import GlowwormSwarmOptimization
+from opt.swarm_intelligence import GreyWolfOptimizer
+from opt.swarm_intelligence import ParticleSwarm
+from opt.swarm_intelligence import SquirrelSearchAlgorithm
+from opt.swarm_intelligence import WhaleOptimizationAlgorithm
+
__version__ = "0.1.2"
+
+__all__: list[str] = [
+ # Classical
+ "BFGS",
+ "LBFGS",
+ "SGD",
+ "ADAGrad",
+ "ADAMOptimization",
+ "AMSGrad",
+ # Base class
+ "AbstractOptimizer",
+ # Gradient-based
+ "AdaDelta",
+ "AdaMax",
+ "AdamW",
+ # Swarm intelligence
+ "AntColony",
+ "ArtificialFishSwarm",
+ # Constrained
+ "AugmentedLagrangian",
+ "BatAlgorithm",
+ "BeeAlgorithm",
+ # Evolutionary
+ "CMAESAlgorithm",
+ "CatSwarmOptimization",
+ # Metaheuristic
+ "CollidingBodiesOptimization",
+ "ConjugateGradient",
+ "CrossEntropyMethod",
+ "CuckooSearch",
+ "CulturalAlgorithm",
+ "DifferentialEvolution",
+ "EagleStrategy",
+ "EstimationOfDistributionAlgorithm",
+ "FireflyAlgorithm",
+ "GeneticAlgorithm",
+ "GlowwormSwarmOptimization",
+ "GreyWolfOptimizer",
+ "HarmonySearch",
+ "HillClimbing",
+ "ImperialistCompetitiveAlgorithm",
+ # Probabilistic
+ "LDAnalysis",
+ "Nadam",
+ "NelderMead",
+ "NesterovAcceleratedGradient",
+ "ParticleFilter",
+ "ParticleSwarm",
+ "ParzenTreeEstimator",
+ "Powell",
+ "RMSprop",
+ "SGDMomentum",
+ "ShuffledFrogLeapingAlgorithm",
+ "SimulatedAnnealing",
+ "SineCosineAlgorithm",
+ "SquirrelSearchAlgorithm",
+ "StochasticDiffusionSearch",
+ "StochasticFractalSearch",
+ "SuccessiveLinearProgramming",
+ "TabuSearch",
+ "TrustRegion",
+ "VariableDepthSearch",
+ "VariableNeighborhoodSearch",
+ "VeryLargeScaleNeighborhood",
+ "WhaleOptimizationAlgorithm",
+]
diff --git a/opt/classical/__init__.py b/opt/classical/__init__.py
new file mode 100644
index 00000000..350572ad
--- /dev/null
+++ b/opt/classical/__init__.py
@@ -0,0 +1,31 @@
+"""Classical optimization algorithms.
+
+This module contains traditional mathematical optimization methods including derivative-based
+and derivative-free approaches. Includes: BFGS, Conjugate Gradient, Hill Climbing, L-BFGS,
+Nelder-Mead, Powell, Simulated Annealing, Tabu Search, and Trust Region methods.
+"""
+
+from __future__ import annotations
+
+from opt.classical.bfgs import BFGS
+from opt.classical.conjugate_gradient import ConjugateGradient
+from opt.classical.hill_climbing import HillClimbing
+from opt.classical.lbfgs import LBFGS
+from opt.classical.nelder_mead import NelderMead
+from opt.classical.powell import Powell
+from opt.classical.simulated_annealing import SimulatedAnnealing
+from opt.classical.tabu_search import TabuSearch
+from opt.classical.trust_region import TrustRegion
+
+
+__all__: list[str] = [
+ "BFGS",
+ "LBFGS",
+ "ConjugateGradient",
+ "HillClimbing",
+ "NelderMead",
+ "Powell",
+ "SimulatedAnnealing",
+ "TabuSearch",
+ "TrustRegion",
+]
diff --git a/opt/bfgs.py b/opt/classical/bfgs.py
similarity index 100%
rename from opt/bfgs.py
rename to opt/classical/bfgs.py
diff --git a/opt/conjugate_gradient.py b/opt/classical/conjugate_gradient.py
similarity index 100%
rename from opt/conjugate_gradient.py
rename to opt/classical/conjugate_gradient.py
diff --git a/opt/hill_climbing.py b/opt/classical/hill_climbing.py
similarity index 100%
rename from opt/hill_climbing.py
rename to opt/classical/hill_climbing.py
diff --git a/opt/lbfgs.py b/opt/classical/lbfgs.py
similarity index 100%
rename from opt/lbfgs.py
rename to opt/classical/lbfgs.py
diff --git a/opt/nelder_mead.py b/opt/classical/nelder_mead.py
similarity index 100%
rename from opt/nelder_mead.py
rename to opt/classical/nelder_mead.py
diff --git a/opt/powell.py b/opt/classical/powell.py
similarity index 100%
rename from opt/powell.py
rename to opt/classical/powell.py
diff --git a/opt/simulated_annealing.py b/opt/classical/simulated_annealing.py
similarity index 100%
rename from opt/simulated_annealing.py
rename to opt/classical/simulated_annealing.py
diff --git a/opt/tabu_search.py b/opt/classical/tabu_search.py
similarity index 100%
rename from opt/tabu_search.py
rename to opt/classical/tabu_search.py
diff --git a/opt/trust_region.py b/opt/classical/trust_region.py
similarity index 100%
rename from opt/trust_region.py
rename to opt/classical/trust_region.py
diff --git a/opt/constrained/__init__.py b/opt/constrained/__init__.py
new file mode 100644
index 00000000..cda535a6
--- /dev/null
+++ b/opt/constrained/__init__.py
@@ -0,0 +1,14 @@
+"""Constrained optimization algorithms.
+
+This module contains optimizers specifically designed for handling optimization problems
+with equality and/or inequality constraints. Includes: Augmented Lagrangian Method
+and Successive Linear Programming.
+"""
+
+from __future__ import annotations
+
+from opt.constrained.augmented_lagrangian_method import AugmentedLagrangian
+from opt.constrained.successive_linear_programming import SuccessiveLinearProgramming
+
+
+__all__: list[str] = ["AugmentedLagrangian", "SuccessiveLinearProgramming"]
diff --git a/opt/augmented_lagrangian_method.py b/opt/constrained/augmented_lagrangian_method.py
similarity index 100%
rename from opt/augmented_lagrangian_method.py
rename to opt/constrained/augmented_lagrangian_method.py
diff --git a/opt/successive_linear_programming.py b/opt/constrained/successive_linear_programming.py
similarity index 100%
rename from opt/successive_linear_programming.py
rename to opt/constrained/successive_linear_programming.py
diff --git a/opt/evolutionary/__init__.py b/opt/evolutionary/__init__.py
new file mode 100644
index 00000000..c7616cec
--- /dev/null
+++ b/opt/evolutionary/__init__.py
@@ -0,0 +1,29 @@
+"""Evolutionary optimization algorithms.
+
+This module contains population-based metaheuristic optimizers inspired by biological
+evolution. Includes: CMA-ES, Cultural Algorithm, Differential Evolution, Estimation of
+Distribution Algorithm, Genetic Algorithm, and Imperialist Competitive Algorithm.
+"""
+
+from __future__ import annotations
+
+from opt.evolutionary.cma_es import CMAESAlgorithm
+from opt.evolutionary.cultural_algorithm import CulturalAlgorithm
+from opt.evolutionary.differential_evolution import DifferentialEvolution
+from opt.evolutionary.estimation_of_distribution_algorithm import (
+ EstimationOfDistributionAlgorithm,
+)
+from opt.evolutionary.genetic_algorithm import GeneticAlgorithm
+from opt.evolutionary.imperialist_competitive_algorithm import (
+ ImperialistCompetitiveAlgorithm,
+)
+
+
+__all__: list[str] = [
+ "CMAESAlgorithm",
+ "CulturalAlgorithm",
+ "DifferentialEvolution",
+ "EstimationOfDistributionAlgorithm",
+ "GeneticAlgorithm",
+ "ImperialistCompetitiveAlgorithm",
+]
diff --git a/opt/cma_es.py b/opt/evolutionary/cma_es.py
similarity index 100%
rename from opt/cma_es.py
rename to opt/evolutionary/cma_es.py
diff --git a/opt/cultural_algorithm.py b/opt/evolutionary/cultural_algorithm.py
similarity index 100%
rename from opt/cultural_algorithm.py
rename to opt/evolutionary/cultural_algorithm.py
diff --git a/opt/differential_evolution.py b/opt/evolutionary/differential_evolution.py
similarity index 100%
rename from opt/differential_evolution.py
rename to opt/evolutionary/differential_evolution.py
diff --git a/opt/estimation_of_distribution_algorithm.py b/opt/evolutionary/estimation_of_distribution_algorithm.py
similarity index 100%
rename from opt/estimation_of_distribution_algorithm.py
rename to opt/evolutionary/estimation_of_distribution_algorithm.py
diff --git a/opt/genetic_algorithm.py b/opt/evolutionary/genetic_algorithm.py
similarity index 100%
rename from opt/genetic_algorithm.py
rename to opt/evolutionary/genetic_algorithm.py
diff --git a/opt/imperialist_competitive_algorithm.py b/opt/evolutionary/imperialist_competitive_algorithm.py
similarity index 100%
rename from opt/imperialist_competitive_algorithm.py
rename to opt/evolutionary/imperialist_competitive_algorithm.py
diff --git a/opt/gradient_based/__init__.py b/opt/gradient_based/__init__.py
new file mode 100644
index 00000000..d92c18c8
--- /dev/null
+++ b/opt/gradient_based/__init__.py
@@ -0,0 +1,34 @@
+"""Gradient-based optimization algorithms.
+
+This module contains optimizers that use gradient information to find optimal solutions.
+Includes: AdaDelta, AdaGrad, AdaMax, AdamW, Adam, AMSGrad, NAdam, Nesterov, RMSprop, SGD.
+"""
+
+from __future__ import annotations
+
+from opt.gradient_based.adadelta import AdaDelta
+from opt.gradient_based.adagrad import ADAGrad
+from opt.gradient_based.adamax import AdaMax
+from opt.gradient_based.adamw import AdamW
+from opt.gradient_based.adaptive_moment_estimation import ADAMOptimization
+from opt.gradient_based.amsgrad import AMSGrad
+from opt.gradient_based.nadam import Nadam
+from opt.gradient_based.nesterov_accelerated_gradient import NesterovAcceleratedGradient
+from opt.gradient_based.rmsprop import RMSprop
+from opt.gradient_based.sgd_momentum import SGDMomentum
+from opt.gradient_based.stochastic_gradient_descent import SGD
+
+
+__all__: list[str] = [
+ "SGD",
+ "ADAGrad",
+ "ADAMOptimization",
+ "AMSGrad",
+ "AdaDelta",
+ "AdaMax",
+ "AdamW",
+ "Nadam",
+ "NesterovAcceleratedGradient",
+ "RMSprop",
+ "SGDMomentum",
+]
diff --git a/opt/adadelta.py b/opt/gradient_based/adadelta.py
similarity index 100%
rename from opt/adadelta.py
rename to opt/gradient_based/adadelta.py
diff --git a/opt/adagrad.py b/opt/gradient_based/adagrad.py
similarity index 100%
rename from opt/adagrad.py
rename to opt/gradient_based/adagrad.py
diff --git a/opt/adamax.py b/opt/gradient_based/adamax.py
similarity index 100%
rename from opt/adamax.py
rename to opt/gradient_based/adamax.py
diff --git a/opt/adamw.py b/opt/gradient_based/adamw.py
similarity index 100%
rename from opt/adamw.py
rename to opt/gradient_based/adamw.py
diff --git a/opt/adaptive_moment_estimation.py b/opt/gradient_based/adaptive_moment_estimation.py
similarity index 100%
rename from opt/adaptive_moment_estimation.py
rename to opt/gradient_based/adaptive_moment_estimation.py
diff --git a/opt/amsgrad.py b/opt/gradient_based/amsgrad.py
similarity index 100%
rename from opt/amsgrad.py
rename to opt/gradient_based/amsgrad.py
diff --git a/opt/nadam.py b/opt/gradient_based/nadam.py
similarity index 100%
rename from opt/nadam.py
rename to opt/gradient_based/nadam.py
diff --git a/opt/nesterov_accelerated_gradient.py b/opt/gradient_based/nesterov_accelerated_gradient.py
similarity index 100%
rename from opt/nesterov_accelerated_gradient.py
rename to opt/gradient_based/nesterov_accelerated_gradient.py
diff --git a/opt/rmsprop.py b/opt/gradient_based/rmsprop.py
similarity index 100%
rename from opt/rmsprop.py
rename to opt/gradient_based/rmsprop.py
diff --git a/opt/sgd_momentum.py b/opt/gradient_based/sgd_momentum.py
similarity index 100%
rename from opt/sgd_momentum.py
rename to opt/gradient_based/sgd_momentum.py
diff --git a/opt/stochastic_gradient_descent.py b/opt/gradient_based/stochastic_gradient_descent.py
similarity index 100%
rename from opt/stochastic_gradient_descent.py
rename to opt/gradient_based/stochastic_gradient_descent.py
diff --git a/opt/metaheuristic/__init__.py b/opt/metaheuristic/__init__.py
new file mode 100644
index 00000000..17f750aa
--- /dev/null
+++ b/opt/metaheuristic/__init__.py
@@ -0,0 +1,42 @@
+"""Metaheuristic optimization algorithms.
+
+This module contains high-level problem-independent algorithmic frameworks that provide
+strategies for exploring search spaces. Includes: Colliding Bodies, Cross-Entropy,
+Eagle Strategy, Harmony Search, Particle Filter, Shuffled Frog Leaping, Sine Cosine,
+Stochastic Diffusion/Fractal Search, and Variable Neighborhood Search variants.
+"""
+
+from __future__ import annotations
+
+from opt.metaheuristic.colliding_bodies_optimization import CollidingBodiesOptimization
+from opt.metaheuristic.cross_entropy_method import CrossEntropyMethod
+from opt.metaheuristic.eagle_strategy import EagleStrategy
+from opt.metaheuristic.harmony_search import HarmonySearch
+from opt.metaheuristic.particle_filter import ParticleFilter
+from opt.metaheuristic.shuffled_frog_leaping_algorithm import (
+ ShuffledFrogLeapingAlgorithm,
+)
+from opt.metaheuristic.sine_cosine_algorithm import SineCosineAlgorithm
+from opt.metaheuristic.stochastic_diffusion_search import StochasticDiffusionSearch
+from opt.metaheuristic.stochastic_fractal_search import StochasticFractalSearch
+from opt.metaheuristic.variable_depth_search import VariableDepthSearch
+from opt.metaheuristic.variable_neighbourhood_search import VariableNeighborhoodSearch
+from opt.metaheuristic.very_large_scale_neighborhood_search import (
+ VeryLargeScaleNeighborhood,
+)
+
+
+__all__: list[str] = [
+ "CollidingBodiesOptimization",
+ "CrossEntropyMethod",
+ "EagleStrategy",
+ "HarmonySearch",
+ "ParticleFilter",
+ "ShuffledFrogLeapingAlgorithm",
+ "SineCosineAlgorithm",
+ "StochasticDiffusionSearch",
+ "StochasticFractalSearch",
+ "VariableDepthSearch",
+ "VariableNeighborhoodSearch",
+ "VeryLargeScaleNeighborhood",
+]
diff --git a/opt/colliding_bodies_optimization.py b/opt/metaheuristic/colliding_bodies_optimization.py
similarity index 100%
rename from opt/colliding_bodies_optimization.py
rename to opt/metaheuristic/colliding_bodies_optimization.py
diff --git a/opt/cross_entropy_method.py b/opt/metaheuristic/cross_entropy_method.py
similarity index 100%
rename from opt/cross_entropy_method.py
rename to opt/metaheuristic/cross_entropy_method.py
diff --git a/opt/eagle_strategy.py b/opt/metaheuristic/eagle_strategy.py
similarity index 100%
rename from opt/eagle_strategy.py
rename to opt/metaheuristic/eagle_strategy.py
diff --git a/opt/harmony_search.py b/opt/metaheuristic/harmony_search.py
similarity index 100%
rename from opt/harmony_search.py
rename to opt/metaheuristic/harmony_search.py
diff --git a/opt/particle_filter.py b/opt/metaheuristic/particle_filter.py
similarity index 100%
rename from opt/particle_filter.py
rename to opt/metaheuristic/particle_filter.py
diff --git a/opt/shuffled_frog_leaping_algorithm.py b/opt/metaheuristic/shuffled_frog_leaping_algorithm.py
similarity index 100%
rename from opt/shuffled_frog_leaping_algorithm.py
rename to opt/metaheuristic/shuffled_frog_leaping_algorithm.py
diff --git a/opt/sine_cosine_algorithm.py b/opt/metaheuristic/sine_cosine_algorithm.py
similarity index 100%
rename from opt/sine_cosine_algorithm.py
rename to opt/metaheuristic/sine_cosine_algorithm.py
diff --git a/opt/stochastic_diffusion_search.py b/opt/metaheuristic/stochastic_diffusion_search.py
similarity index 100%
rename from opt/stochastic_diffusion_search.py
rename to opt/metaheuristic/stochastic_diffusion_search.py
diff --git a/opt/stochastic_fractal_search.py b/opt/metaheuristic/stochastic_fractal_search.py
similarity index 100%
rename from opt/stochastic_fractal_search.py
rename to opt/metaheuristic/stochastic_fractal_search.py
diff --git a/opt/variable_depth_search.py b/opt/metaheuristic/variable_depth_search.py
similarity index 100%
rename from opt/variable_depth_search.py
rename to opt/metaheuristic/variable_depth_search.py
diff --git a/opt/variable_neighbourhood_search.py b/opt/metaheuristic/variable_neighbourhood_search.py
similarity index 100%
rename from opt/variable_neighbourhood_search.py
rename to opt/metaheuristic/variable_neighbourhood_search.py
diff --git a/opt/very_large_scale_neighborhood_search.py b/opt/metaheuristic/very_large_scale_neighborhood_search.py
similarity index 100%
rename from opt/very_large_scale_neighborhood_search.py
rename to opt/metaheuristic/very_large_scale_neighborhood_search.py
diff --git a/opt/probabilistic/__init__.py b/opt/probabilistic/__init__.py
new file mode 100644
index 00000000..04f71e23
--- /dev/null
+++ b/opt/probabilistic/__init__.py
@@ -0,0 +1,14 @@
+"""Probabilistic optimization algorithms.
+
+This module contains optimizers that use probabilistic models and statistical methods
+to guide the search process. Includes: Parzen Tree Estimator (TPE) and
+Linear Discriminant Analysis based optimization.
+"""
+
+from __future__ import annotations
+
+from opt.probabilistic.linear_discriminant_analysis import LDAnalysis
+from opt.probabilistic.parzen_tree_stimator import ParzenTreeEstimator
+
+
+__all__: list[str] = ["LDAnalysis", "ParzenTreeEstimator"]
diff --git a/opt/linear_discriminant_analysis.py b/opt/probabilistic/linear_discriminant_analysis.py
similarity index 100%
rename from opt/linear_discriminant_analysis.py
rename to opt/probabilistic/linear_discriminant_analysis.py
diff --git a/opt/parzen_tree_stimator.py b/opt/probabilistic/parzen_tree_stimator.py
similarity index 100%
rename from opt/parzen_tree_stimator.py
rename to opt/probabilistic/parzen_tree_stimator.py
diff --git a/opt/swarm_intelligence/__init__.py b/opt/swarm_intelligence/__init__.py
new file mode 100644
index 00000000..8acf40e5
--- /dev/null
+++ b/opt/swarm_intelligence/__init__.py
@@ -0,0 +1,40 @@
+"""Swarm intelligence optimization algorithms.
+
+This module contains nature-inspired optimizers based on collective behavior of
+decentralized, self-organized systems. Includes: Ant Colony, Artificial Fish Swarm,
+Bat, Bee, Cat Swarm, Cuckoo Search, Firefly, Glowworm Swarm, Grey Wolf, Particle Swarm,
+Squirrel Search, and Whale Optimization algorithms.
+"""
+
+from __future__ import annotations
+
+from opt.swarm_intelligence.ant_colony import AntColony
+from opt.swarm_intelligence.artificial_fish_swarm_algorithm import ArtificialFishSwarm
+from opt.swarm_intelligence.bat_algorithm import BatAlgorithm
+from opt.swarm_intelligence.bee_algorithm import BeeAlgorithm
+from opt.swarm_intelligence.cat_swarm_optimization import CatSwarmOptimization
+from opt.swarm_intelligence.cuckoo_search import CuckooSearch
+from opt.swarm_intelligence.firefly_algorithm import FireflyAlgorithm
+from opt.swarm_intelligence.glowworm_swarm_optimization import GlowwormSwarmOptimization
+from opt.swarm_intelligence.grey_wolf_optimizer import GreyWolfOptimizer
+from opt.swarm_intelligence.particle_swarm import ParticleSwarm
+from opt.swarm_intelligence.squirrel_search import SquirrelSearchAlgorithm
+from opt.swarm_intelligence.whale_optimization_algorithm import (
+ WhaleOptimizationAlgorithm,
+)
+
+
+__all__: list[str] = [
+ "AntColony",
+ "ArtificialFishSwarm",
+ "BatAlgorithm",
+ "BeeAlgorithm",
+ "CatSwarmOptimization",
+ "CuckooSearch",
+ "FireflyAlgorithm",
+ "GlowwormSwarmOptimization",
+ "GreyWolfOptimizer",
+ "ParticleSwarm",
+ "SquirrelSearchAlgorithm",
+ "WhaleOptimizationAlgorithm",
+]
diff --git a/opt/ant_colony.py b/opt/swarm_intelligence/ant_colony.py
similarity index 100%
rename from opt/ant_colony.py
rename to opt/swarm_intelligence/ant_colony.py
diff --git a/opt/artificial_fish_swarm_algorithm.py b/opt/swarm_intelligence/artificial_fish_swarm_algorithm.py
similarity index 100%
rename from opt/artificial_fish_swarm_algorithm.py
rename to opt/swarm_intelligence/artificial_fish_swarm_algorithm.py
diff --git a/opt/bat_algorithm.py b/opt/swarm_intelligence/bat_algorithm.py
similarity index 100%
rename from opt/bat_algorithm.py
rename to opt/swarm_intelligence/bat_algorithm.py
diff --git a/opt/bee_algorithm.py b/opt/swarm_intelligence/bee_algorithm.py
similarity index 98%
rename from opt/bee_algorithm.py
rename to opt/swarm_intelligence/bee_algorithm.py
index 57124fbf..df6f2181 100644
--- a/opt/bee_algorithm.py
+++ b/opt/swarm_intelligence/bee_algorithm.py
@@ -129,7 +129,7 @@ def search(self) -> tuple[np.ndarray, float]:
# Onlooker Bee Phase
for i in range(self.population_size):
self.seed += 1
- if np.random.default(self.seed).random() < self.prob[i]:
+ if np.random.default_rng(self.seed).random() < self.prob[i]:
candidate_solution = self.population[i] + np.random.default_rng(
self.seed + 1
).uniform(-1, 1, self.dim)
diff --git a/opt/cat_swarm_optimization.py b/opt/swarm_intelligence/cat_swarm_optimization.py
similarity index 100%
rename from opt/cat_swarm_optimization.py
rename to opt/swarm_intelligence/cat_swarm_optimization.py
diff --git a/opt/cuckoo_search.py b/opt/swarm_intelligence/cuckoo_search.py
similarity index 100%
rename from opt/cuckoo_search.py
rename to opt/swarm_intelligence/cuckoo_search.py
diff --git a/opt/firefly_algorithm.py b/opt/swarm_intelligence/firefly_algorithm.py
similarity index 100%
rename from opt/firefly_algorithm.py
rename to opt/swarm_intelligence/firefly_algorithm.py
diff --git a/opt/glowworm_swarm_optimization.py b/opt/swarm_intelligence/glowworm_swarm_optimization.py
similarity index 100%
rename from opt/glowworm_swarm_optimization.py
rename to opt/swarm_intelligence/glowworm_swarm_optimization.py
diff --git a/opt/grey_wolf_optimizer.py b/opt/swarm_intelligence/grey_wolf_optimizer.py
similarity index 100%
rename from opt/grey_wolf_optimizer.py
rename to opt/swarm_intelligence/grey_wolf_optimizer.py
diff --git a/opt/particle_swarm.py b/opt/swarm_intelligence/particle_swarm.py
similarity index 100%
rename from opt/particle_swarm.py
rename to opt/swarm_intelligence/particle_swarm.py
diff --git a/opt/squirrel_search.py b/opt/swarm_intelligence/squirrel_search.py
similarity index 100%
rename from opt/squirrel_search.py
rename to opt/swarm_intelligence/squirrel_search.py
diff --git a/opt/whale_optimization_algorithm.py b/opt/swarm_intelligence/whale_optimization_algorithm.py
similarity index 100%
rename from opt/whale_optimization_algorithm.py
rename to opt/swarm_intelligence/whale_optimization_algorithm.py
diff --git a/opt/test/__init__.py b/opt/test/__init__.py
new file mode 100644
index 00000000..f39181c0
--- /dev/null
+++ b/opt/test/__init__.py
@@ -0,0 +1,3 @@
+"""Tests for the useful-optimizer package."""
+
+from __future__ import annotations
diff --git a/opt/test/test_optimizers.py b/opt/test/test_optimizers.py
new file mode 100644
index 00000000..b316c4de
--- /dev/null
+++ b/opt/test/test_optimizers.py
@@ -0,0 +1,417 @@
+"""Tests for all optimizer classes."""
+
+from __future__ import annotations
+
+import numpy as np
+import pytest
+
+from opt import BFGS
+from opt import LBFGS
+from opt import SGD
+from opt import ADAGrad
+from opt import ADAMOptimization
+from opt import AMSGrad
+from opt import AbstractOptimizer
+from opt import AdaDelta
+from opt import AdaMax
+from opt import AdamW
+from opt import AntColony
+from opt import ArtificialFishSwarm
+from opt import AugmentedLagrangian
+from opt import BatAlgorithm
+from opt import BeeAlgorithm
+from opt import CMAESAlgorithm
+from opt import CatSwarmOptimization
+from opt import CollidingBodiesOptimization
+from opt import ConjugateGradient
+from opt import CrossEntropyMethod
+from opt import CuckooSearch
+from opt import CulturalAlgorithm
+from opt import DifferentialEvolution
+from opt import EagleStrategy
+from opt import EstimationOfDistributionAlgorithm
+from opt import FireflyAlgorithm
+from opt import GeneticAlgorithm
+from opt import GlowwormSwarmOptimization
+from opt import GreyWolfOptimizer
+from opt import HarmonySearch
+from opt import HillClimbing
+from opt import ImperialistCompetitiveAlgorithm
+from opt import LDAnalysis
+from opt import Nadam
+from opt import NelderMead
+from opt import NesterovAcceleratedGradient
+from opt import ParticleFilter
+from opt import ParticleSwarm
+from opt import ParzenTreeEstimator
+from opt import Powell
+from opt import RMSprop
+from opt import SGDMomentum
+from opt import ShuffledFrogLeapingAlgorithm
+from opt import SimulatedAnnealing
+from opt import SineCosineAlgorithm
+from opt import SquirrelSearchAlgorithm
+from opt import StochasticDiffusionSearch
+from opt import StochasticFractalSearch
+from opt import SuccessiveLinearProgramming
+from opt import TabuSearch
+from opt import TrustRegion
+from opt import VariableDepthSearch
+from opt import VariableNeighborhoodSearch
+from opt import VeryLargeScaleNeighborhood
+from opt import WhaleOptimizationAlgorithm
+from opt.benchmark.functions import shifted_ackley
+from opt.benchmark.functions import sphere
+
+
+# List of all optimizer classes for parametrized testing
+SWARM_OPTIMIZERS = [
+ AntColony,
+ ArtificialFishSwarm,
+ # BatAlgorithm excluded - requires n_bats parameter (tested separately)
+ BeeAlgorithm,
+ CatSwarmOptimization,
+ CuckooSearch,
+ FireflyAlgorithm,
+ GlowwormSwarmOptimization,
+ GreyWolfOptimizer,
+ ParticleSwarm,
+ SquirrelSearchAlgorithm,
+ WhaleOptimizationAlgorithm,
+]
+
+EVOLUTIONARY_OPTIMIZERS = [
+ CMAESAlgorithm,
+ CulturalAlgorithm,
+ DifferentialEvolution,
+ EstimationOfDistributionAlgorithm,
+ GeneticAlgorithm,
+ ImperialistCompetitiveAlgorithm,
+]
+
+GRADIENT_OPTIMIZERS = [
+ AdaDelta,
+ ADAGrad,
+ AdaMax,
+ AdamW,
+ ADAMOptimization,
+ AMSGrad,
+ Nadam,
+ NesterovAcceleratedGradient,
+ RMSprop,
+ SGD,
+ SGDMomentum,
+]
+
+CLASSICAL_OPTIMIZERS = [
+ BFGS,
+ ConjugateGradient,
+ HillClimbing,
+ LBFGS,
+ NelderMead,
+ Powell,
+ SimulatedAnnealing,
+ TabuSearch,
+ TrustRegion,
+]
+
+METAHEURISTIC_OPTIMIZERS = [
+ CollidingBodiesOptimization,
+ CrossEntropyMethod,
+ EagleStrategy,
+ HarmonySearch,
+ ParticleFilter,
+ ShuffledFrogLeapingAlgorithm,
+ SineCosineAlgorithm,
+ StochasticDiffusionSearch,
+ StochasticFractalSearch,
+ VariableDepthSearch,
+ VariableNeighborhoodSearch,
+ VeryLargeScaleNeighborhood,
+]
+
+CONSTRAINED_OPTIMIZERS = [AugmentedLagrangian, SuccessiveLinearProgramming]
+
+PROBABILISTIC_OPTIMIZERS = [LDAnalysis, ParzenTreeEstimator]
+
+ALL_OPTIMIZERS = (
+ SWARM_OPTIMIZERS
+ + EVOLUTIONARY_OPTIMIZERS
+ + GRADIENT_OPTIMIZERS
+ + CLASSICAL_OPTIMIZERS
+ + METAHEURISTIC_OPTIMIZERS
+ + CONSTRAINED_OPTIMIZERS
+ + PROBABILISTIC_OPTIMIZERS
+)
+
+
+class TestAbstractOptimizer:
+ """Tests for the AbstractOptimizer base class."""
+
+ def test_abstract_optimizer_cannot_be_instantiated(self) -> None:
+ """Test that AbstractOptimizer cannot be instantiated directly."""
+ with pytest.raises(TypeError):
+ AbstractOptimizer( # type: ignore[abstract]
+ func=sphere, lower_bound=-5, upper_bound=5, dim=2
+ )
+
+ def test_all_optimizers_inherit_from_abstract(self) -> None:
+ """Test that all optimizers inherit from AbstractOptimizer."""
+ for optimizer_class in ALL_OPTIMIZERS:
+ assert issubclass(optimizer_class, AbstractOptimizer)
+
+
+class TestOptimizerInstantiation:
+ """Tests for optimizer instantiation."""
+
+ @pytest.mark.parametrize("optimizer_class", ALL_OPTIMIZERS)
+ def test_optimizer_instantiation(
+ self, optimizer_class: type[AbstractOptimizer]
+ ) -> None:
+ """Test that all optimizers can be instantiated."""
+ optimizer = optimizer_class(
+ func=sphere, lower_bound=-5, upper_bound=5, dim=2, max_iter=10
+ )
+ assert optimizer is not None
+ assert optimizer.func == sphere
+ assert optimizer.lower_bound == -5
+ assert optimizer.upper_bound == 5
+ assert optimizer.dim == 2
+ assert optimizer.max_iter == 10
+
+
+class TestOptimizerSearch:
+ """Tests for optimizer search functionality."""
+
+ @pytest.mark.parametrize("optimizer_class", SWARM_OPTIMIZERS)
+ def test_swarm_optimizer_search(
+ self, optimizer_class: type[AbstractOptimizer]
+ ) -> None:
+ """Test that swarm optimizers can perform search."""
+ optimizer = optimizer_class(
+ func=shifted_ackley,
+ lower_bound=-2.768,
+ upper_bound=2.768,
+ dim=2,
+ max_iter=20,
+ )
+ solution, fitness = optimizer.search()
+ assert isinstance(solution, np.ndarray)
+ assert isinstance(fitness, float)
+ assert solution.shape == (2,)
+
+ @pytest.mark.parametrize("optimizer_class", EVOLUTIONARY_OPTIMIZERS)
+ def test_evolutionary_optimizer_search(
+ self, optimizer_class: type[AbstractOptimizer]
+ ) -> None:
+ """Test that evolutionary optimizers can perform search."""
+ optimizer = optimizer_class(
+ func=shifted_ackley,
+ lower_bound=-2.768,
+ upper_bound=2.768,
+ dim=2,
+ max_iter=20,
+ )
+ solution, fitness = optimizer.search()
+ assert isinstance(solution, np.ndarray)
+ assert isinstance(fitness, float)
+ assert solution.shape == (2,)
+
+ @pytest.mark.parametrize("optimizer_class", GRADIENT_OPTIMIZERS)
+ def test_gradient_optimizer_search(
+ self, optimizer_class: type[AbstractOptimizer]
+ ) -> None:
+ """Test that gradient optimizers can perform search."""
+ optimizer = optimizer_class(
+ func=shifted_ackley,
+ lower_bound=-2.768,
+ upper_bound=2.768,
+ dim=2,
+ max_iter=20,
+ )
+ solution, fitness = optimizer.search()
+ assert isinstance(solution, np.ndarray)
+ assert isinstance(fitness, float)
+ assert solution.shape == (2,)
+
+ @pytest.mark.parametrize("optimizer_class", CLASSICAL_OPTIMIZERS)
+ def test_classical_optimizer_search(
+ self, optimizer_class: type[AbstractOptimizer]
+ ) -> None:
+ """Test that classical optimizers can perform search."""
+ optimizer = optimizer_class(
+ func=shifted_ackley,
+ lower_bound=-2.768,
+ upper_bound=2.768,
+ dim=2,
+ max_iter=20,
+ )
+ solution, fitness = optimizer.search()
+ assert isinstance(solution, np.ndarray)
+ assert isinstance(fitness, float)
+ assert solution.shape == (2,)
+
+ @pytest.mark.parametrize("optimizer_class", METAHEURISTIC_OPTIMIZERS)
+ def test_metaheuristic_optimizer_search(
+ self, optimizer_class: type[AbstractOptimizer]
+ ) -> None:
+ """Test that metaheuristic optimizers can perform search."""
+ optimizer = optimizer_class(
+ func=shifted_ackley,
+ lower_bound=-2.768,
+ upper_bound=2.768,
+ dim=2,
+ max_iter=20,
+ )
+ solution, fitness = optimizer.search()
+ assert isinstance(solution, np.ndarray)
+ assert isinstance(fitness, float)
+ assert solution.shape == (2,)
+
+ @pytest.mark.parametrize("optimizer_class", CONSTRAINED_OPTIMIZERS)
+ def test_constrained_optimizer_search(
+ self, optimizer_class: type[AbstractOptimizer]
+ ) -> None:
+ """Test that constrained optimizers can perform search."""
+ optimizer = optimizer_class(
+ func=shifted_ackley,
+ lower_bound=-2.768,
+ upper_bound=2.768,
+ dim=2,
+ max_iter=20,
+ )
+ solution, fitness = optimizer.search()
+ assert isinstance(solution, np.ndarray)
+ assert isinstance(fitness, float)
+ assert solution.shape == (2,)
+
+ @pytest.mark.parametrize("optimizer_class", PROBABILISTIC_OPTIMIZERS)
+ def test_probabilistic_optimizer_search(
+ self, optimizer_class: type[AbstractOptimizer]
+ ) -> None:
+ """Test that probabilistic optimizers can perform search."""
+ optimizer = optimizer_class(
+ func=shifted_ackley,
+ lower_bound=-2.768,
+ upper_bound=2.768,
+ dim=2,
+ max_iter=20,
+ )
+ solution, fitness = optimizer.search()
+ assert isinstance(solution, np.ndarray)
+ assert isinstance(fitness, float)
+ assert solution.shape == (2,)
+
+
+class TestSpecialOptimizers:
+ """Tests for optimizers with special parameter requirements."""
+
+ def test_bat_algorithm_with_n_bats(self) -> None:
+ """Test BatAlgorithm with required n_bats parameter."""
+ optimizer = BatAlgorithm(
+ func=shifted_ackley,
+ lower_bound=-2.768,
+ upper_bound=2.768,
+ dim=2,
+ n_bats=10,
+ max_iter=20,
+ )
+ solution, fitness = optimizer.search()
+ assert isinstance(solution, np.ndarray)
+ assert isinstance(fitness, float)
+ assert solution.shape == (2,)
+
+ def test_bat_algorithm_instantiation(self) -> None:
+ """Test that BatAlgorithm can be instantiated with n_bats parameter."""
+ optimizer = BatAlgorithm(
+ func=sphere, lower_bound=-5, upper_bound=5, dim=2, n_bats=5, max_iter=10
+ )
+ assert optimizer is not None
+ assert optimizer.func == sphere
+ assert optimizer.lower_bound == -5
+ assert optimizer.upper_bound == 5
+ assert optimizer.dim == 2
+ assert optimizer.max_iter == 10
+
+
+class TestBenchmarkFunctions:
+ """Tests for benchmark functions."""
+
+ def test_sphere_function(self) -> None:
+ """Test sphere function at known points."""
+ assert sphere(np.array([0, 0])) == 0
+ assert sphere(np.array([1, 0])) == 1
+ assert sphere(np.array([1, 1])) == 2
+
+ def test_shifted_ackley_function(self) -> None:
+ """Test shifted_ackley function returns float."""
+ result = shifted_ackley(np.array([0, 0]))
+ assert isinstance(result, float)
+
+
+class TestCategoricalImports:
+ """Tests for categorical module imports."""
+
+ def test_gradient_based_import(self) -> None:
+ """Test importing from gradient_based submodule."""
+ from opt.gradient_based import AdamW
+ from opt.gradient_based import SGDMomentum
+
+ assert AdamW is not None
+ assert SGDMomentum is not None
+
+ def test_swarm_intelligence_import(self) -> None:
+ """Test importing from swarm_intelligence submodule."""
+ from opt.swarm_intelligence import AntColony
+ from opt.swarm_intelligence import ParticleSwarm
+
+ assert ParticleSwarm is not None
+ assert AntColony is not None
+
+ def test_evolutionary_import(self) -> None:
+ """Test importing from evolutionary submodule."""
+ from opt.evolutionary import DifferentialEvolution
+ from opt.evolutionary import GeneticAlgorithm
+
+ assert GeneticAlgorithm is not None
+ assert DifferentialEvolution is not None
+
+ def test_classical_import(self) -> None:
+ """Test importing from classical submodule."""
+ from opt.classical import BFGS
+ from opt.classical import NelderMead
+
+ assert BFGS is not None
+ assert NelderMead is not None
+
+ def test_metaheuristic_import(self) -> None:
+ """Test importing from metaheuristic submodule."""
+ from opt.metaheuristic import CrossEntropyMethod
+ from opt.metaheuristic import HarmonySearch
+
+ assert HarmonySearch is not None
+ assert CrossEntropyMethod is not None
+
+ def test_constrained_import(self) -> None:
+ """Test importing from constrained submodule."""
+ from opt.constrained import AugmentedLagrangian
+
+ assert AugmentedLagrangian is not None
+
+ def test_probabilistic_import(self) -> None:
+ """Test importing from probabilistic submodule."""
+ from opt.probabilistic import ParzenTreeEstimator
+
+ assert ParzenTreeEstimator is not None
+
+ def test_backward_compatible_import(self) -> None:
+ """Test backward compatible imports from root opt module."""
+ from opt import BFGS
+ from opt import AdamW
+ from opt import HarmonySearch
+ from opt import ParticleSwarm
+
+ assert ParticleSwarm is not None
+ assert AdamW is not None
+ assert BFGS is not None
+ assert HarmonySearch is not None
diff --git a/poetry.lock b/poetry.lock
deleted file mode 100644
index 052901e2..00000000
--- a/poetry.lock
+++ /dev/null
@@ -1,187 +0,0 @@
-# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
-
-[[package]]
-name = "joblib"
-version = "1.4.2"
-description = "Lightweight pipelining with Python functions"
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "joblib-1.4.2-py3-none-any.whl", hash = "sha256:06d478d5674cbc267e7496a410ee875abd68e4340feff4490bcb7afb88060ae6"},
- {file = "joblib-1.4.2.tar.gz", hash = "sha256:2382c5816b2636fbd20a09e0f4e9dad4736765fdfb7dca582943b9c1366b3f0e"},
-]
-
-[[package]]
-name = "numpy"
-version = "1.26.4"
-description = "Fundamental package for array computing in Python"
-optional = false
-python-versions = ">=3.9"
-files = [
- {file = "numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0"},
- {file = "numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a"},
- {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4"},
- {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f"},
- {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a"},
- {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2"},
- {file = "numpy-1.26.4-cp310-cp310-win32.whl", hash = "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07"},
- {file = "numpy-1.26.4-cp310-cp310-win_amd64.whl", hash = "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5"},
- {file = "numpy-1.26.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c66707fabe114439db9068ee468c26bbdf909cac0fb58686a42a24de1760c71"},
- {file = "numpy-1.26.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:edd8b5fe47dab091176d21bb6de568acdd906d1887a4584a15a9a96a1dca06ef"},
- {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab55401287bfec946ced39700c053796e7cc0e3acbef09993a9ad2adba6ca6e"},
- {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:666dbfb6ec68962c033a450943ded891bed2d54e6755e35e5835d63f4f6931d5"},
- {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:96ff0b2ad353d8f990b63294c8986f1ec3cb19d749234014f4e7eb0112ceba5a"},
- {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:60dedbb91afcbfdc9bc0b1f3f402804070deed7392c23eb7a7f07fa857868e8a"},
- {file = "numpy-1.26.4-cp311-cp311-win32.whl", hash = "sha256:1af303d6b2210eb850fcf03064d364652b7120803a0b872f5211f5234b399f20"},
- {file = "numpy-1.26.4-cp311-cp311-win_amd64.whl", hash = "sha256:cd25bcecc4974d09257ffcd1f098ee778f7834c3ad767fe5db785be9a4aa9cb2"},
- {file = "numpy-1.26.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b3ce300f3644fb06443ee2222c2201dd3a89ea6040541412b8fa189341847218"},
- {file = "numpy-1.26.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:03a8c78d01d9781b28a6989f6fa1bb2c4f2d51201cf99d3dd875df6fbd96b23b"},
- {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fad7dcb1aac3c7f0584a5a8133e3a43eeb2fe127f47e3632d43d677c66c102b"},
- {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675d61ffbfa78604709862923189bad94014bef562cc35cf61d3a07bba02a7ed"},
- {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab47dbe5cc8210f55aa58e4805fe224dac469cde56b9f731a4c098b91917159a"},
- {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1dda2e7b4ec9dd512f84935c5f126c8bd8b9f2fc001e9f54af255e8c5f16b0e0"},
- {file = "numpy-1.26.4-cp312-cp312-win32.whl", hash = "sha256:50193e430acfc1346175fcbdaa28ffec49947a06918b7b92130744e81e640110"},
- {file = "numpy-1.26.4-cp312-cp312-win_amd64.whl", hash = "sha256:08beddf13648eb95f8d867350f6a018a4be2e5ad54c8d8caed89ebca558b2818"},
- {file = "numpy-1.26.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7349ab0fa0c429c82442a27a9673fc802ffdb7c7775fad780226cb234965e53c"},
- {file = "numpy-1.26.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:52b8b60467cd7dd1e9ed082188b4e6bb35aa5cdd01777621a1658910745b90be"},
- {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5241e0a80d808d70546c697135da2c613f30e28251ff8307eb72ba696945764"},
- {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f870204a840a60da0b12273ef34f7051e98c3b5961b61b0c2c1be6dfd64fbcd3"},
- {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:679b0076f67ecc0138fd2ede3a8fd196dddc2ad3254069bcb9faf9a79b1cebcd"},
- {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:47711010ad8555514b434df65f7d7b076bb8261df1ca9bb78f53d3b2db02e95c"},
- {file = "numpy-1.26.4-cp39-cp39-win32.whl", hash = "sha256:a354325ee03388678242a4d7ebcd08b5c727033fcff3b2f536aea978e15ee9e6"},
- {file = "numpy-1.26.4-cp39-cp39-win_amd64.whl", hash = "sha256:3373d5d70a5fe74a2c1bb6d2cfd9609ecf686d47a2d7b1d37a8f3b6bf6003aea"},
- {file = "numpy-1.26.4-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:afedb719a9dcfc7eaf2287b839d8198e06dcd4cb5d276a3df279231138e83d30"},
- {file = "numpy-1.26.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95a7476c59002f2f6c590b9b7b998306fba6a5aa646b1e22ddfeaf8f78c3a29c"},
- {file = "numpy-1.26.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7e50d0a0cc3189f9cb0aeb3a6a6af18c16f59f004b866cd2be1c14b36134a4a0"},
- {file = "numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010"},
-]
-
-[[package]]
-name = "ruff"
-version = "0.9.0"
-description = "An extremely fast Python linter and code formatter, written in Rust."
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "ruff-0.9.0-py3-none-linux_armv6l.whl", hash = "sha256:949b3513f931741e006cf267bf89611edff04e1f012013424022add3ce78f319"},
- {file = "ruff-0.9.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:99fbcb8c7fe94ae1e462ab2a1ef17cb20b25fb6438b9f198b1bcf5207a0a7916"},
- {file = "ruff-0.9.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:0b022afd8eb0fcfce1e0adec84322abf4d6ce3cd285b3b99c4f17aae7decf749"},
- {file = "ruff-0.9.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:336567ce92c9ca8ec62780d07b5fa11fbc881dc7bb40958f93a7d621e7ab4589"},
- {file = "ruff-0.9.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d338336c44bda602dc8e8766836ac0441e5b0dfeac3af1bd311a97ebaf087a75"},
- {file = "ruff-0.9.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d9b3ececf523d733e90b540e7afcc0494189e8999847f8855747acd5a9a8c45f"},
- {file = "ruff-0.9.0-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:a11c0872a31232e473e2e0e2107f3d294dbadd2f83fb281c3eb1c22a24866924"},
- {file = "ruff-0.9.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b5fd06220c17a9cc0dc7fc6552f2ac4db74e8e8bff9c401d160ac59d00566f54"},
- {file = "ruff-0.9.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0457e775c74bf3976243f910805242b7dcd389e1d440deccbd1194ca17a5728c"},
- {file = "ruff-0.9.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05415599bbcb318f730ea1b46a39e4fbf71f6a63fdbfa1dda92efb55f19d7ecf"},
- {file = "ruff-0.9.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:fbf9864b009e43cfc1c8bed1a6a4c529156913105780af4141ca4342148517f5"},
- {file = "ruff-0.9.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:37b3da222b12e2bb2ce628e02586ab4846b1ed7f31f42a5a0683b213453b2d49"},
- {file = "ruff-0.9.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:733c0fcf2eb0c90055100b4ed1af9c9d87305b901a8feb6a0451fa53ed88199d"},
- {file = "ruff-0.9.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:8221a454bfe5ccdf8017512fd6bb60e6ec30f9ea252b8a80e5b73619f6c3cefd"},
- {file = "ruff-0.9.0-py3-none-win32.whl", hash = "sha256:d345f2178afd192c7991ddee59155c58145e12ad81310b509bd2e25c5b0247b3"},
- {file = "ruff-0.9.0-py3-none-win_amd64.whl", hash = "sha256:0cbc0905d94d21305872f7f8224e30f4bbcd532bc21b2225b2446d8fc7220d19"},
- {file = "ruff-0.9.0-py3-none-win_arm64.whl", hash = "sha256:7b1148771c6ca88f820d761350a053a5794bc58e0867739ea93eb5e41ad978cd"},
- {file = "ruff-0.9.0.tar.gz", hash = "sha256:143f68fa5560ecf10fc49878b73cee3eab98b777fcf43b0e62d43d42f5ef9d8b"},
-]
-
-[[package]]
-name = "scikit-learn"
-version = "1.5.1"
-description = "A set of python modules for machine learning and data mining"
-optional = false
-python-versions = ">=3.9"
-files = [
- {file = "scikit_learn-1.5.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:781586c414f8cc58e71da4f3d7af311e0505a683e112f2f62919e3019abd3745"},
- {file = "scikit_learn-1.5.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:f5b213bc29cc30a89a3130393b0e39c847a15d769d6e59539cd86b75d276b1a7"},
- {file = "scikit_learn-1.5.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ff4ba34c2abff5ec59c803ed1d97d61b036f659a17f55be102679e88f926fac"},
- {file = "scikit_learn-1.5.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:161808750c267b77b4a9603cf9c93579c7a74ba8486b1336034c2f1579546d21"},
- {file = "scikit_learn-1.5.1-cp310-cp310-win_amd64.whl", hash = "sha256:10e49170691514a94bb2e03787aa921b82dbc507a4ea1f20fd95557862c98dc1"},
- {file = "scikit_learn-1.5.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:154297ee43c0b83af12464adeab378dee2d0a700ccd03979e2b821e7dd7cc1c2"},
- {file = "scikit_learn-1.5.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:b5e865e9bd59396220de49cb4a57b17016256637c61b4c5cc81aaf16bc123bbe"},
- {file = "scikit_learn-1.5.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:909144d50f367a513cee6090873ae582dba019cb3fca063b38054fa42704c3a4"},
- {file = "scikit_learn-1.5.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:689b6f74b2c880276e365fe84fe4f1befd6a774f016339c65655eaff12e10cbf"},
- {file = "scikit_learn-1.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:9a07f90846313a7639af6a019d849ff72baadfa4c74c778821ae0fad07b7275b"},
- {file = "scikit_learn-1.5.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5944ce1faada31c55fb2ba20a5346b88e36811aab504ccafb9f0339e9f780395"},
- {file = "scikit_learn-1.5.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:0828673c5b520e879f2af6a9e99eee0eefea69a2188be1ca68a6121b809055c1"},
- {file = "scikit_learn-1.5.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:508907e5f81390e16d754e8815f7497e52139162fd69c4fdbd2dfa5d6cc88915"},
- {file = "scikit_learn-1.5.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97625f217c5c0c5d0505fa2af28ae424bd37949bb2f16ace3ff5f2f81fb4498b"},
- {file = "scikit_learn-1.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:da3f404e9e284d2b0a157e1b56b6566a34eb2798205cba35a211df3296ab7a74"},
- {file = "scikit_learn-1.5.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:88e0672c7ac21eb149d409c74cc29f1d611d5158175846e7a9c2427bd12b3956"},
- {file = "scikit_learn-1.5.1-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:7b073a27797a283187a4ef4ee149959defc350b46cbf63a84d8514fe16b69855"},
- {file = "scikit_learn-1.5.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b59e3e62d2be870e5c74af4e793293753565c7383ae82943b83383fdcf5cc5c1"},
- {file = "scikit_learn-1.5.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bd8d3a19d4bd6dc5a7d4f358c8c3a60934dc058f363c34c0ac1e9e12a31421d"},
- {file = "scikit_learn-1.5.1-cp39-cp39-win_amd64.whl", hash = "sha256:5f57428de0c900a98389c4a433d4a3cf89de979b3aa24d1c1d251802aa15e44d"},
- {file = "scikit_learn-1.5.1.tar.gz", hash = "sha256:0ea5d40c0e3951df445721927448755d3fe1d80833b0b7308ebff5d2a45e6414"},
-]
-
-[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.23)", "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-gallery (>=0.16.0)", "sphinx-prompt (>=1.4.0)", "sphinx-remove-toctrees (>=1.0.0.post1)", "sphinxcontrib-sass (>=0.3.4)", "sphinxext-opengraph (>=0.9.1)"]
-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.23)", "pooch (>=1.6.0)", "pyamg (>=4.0.0)", "pyarrow (>=12.0.0)", "pytest (>=7.1.2)", "pytest-cov (>=2.9.0)", "ruff (>=0.2.1)", "scikit-image (>=0.17.2)"]
-
-[[package]]
-name = "scipy"
-version = "1.14.0"
-description = "Fundamental algorithms for scientific computing in Python"
-optional = false
-python-versions = ">=3.10"
-files = [
- {file = "scipy-1.14.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7e911933d54ead4d557c02402710c2396529540b81dd554fc1ba270eb7308484"},
- {file = "scipy-1.14.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:687af0a35462402dd851726295c1a5ae5f987bd6e9026f52e9505994e2f84ef6"},
- {file = "scipy-1.14.0-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:07e179dc0205a50721022344fb85074f772eadbda1e1b3eecdc483f8033709b7"},
- {file = "scipy-1.14.0-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:6a9c9a9b226d9a21e0a208bdb024c3982932e43811b62d202aaf1bb59af264b1"},
- {file = "scipy-1.14.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:076c27284c768b84a45dcf2e914d4000aac537da74236a0d45d82c6fa4b7b3c0"},
- {file = "scipy-1.14.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42470ea0195336df319741e230626b6225a740fd9dce9642ca13e98f667047c0"},
- {file = "scipy-1.14.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:176c6f0d0470a32f1b2efaf40c3d37a24876cebf447498a4cefb947a79c21e9d"},
- {file = "scipy-1.14.0-cp310-cp310-win_amd64.whl", hash = "sha256:ad36af9626d27a4326c8e884917b7ec321d8a1841cd6dacc67d2a9e90c2f0359"},
- {file = "scipy-1.14.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6d056a8709ccda6cf36cdd2eac597d13bc03dba38360f418560a93050c76a16e"},
- {file = "scipy-1.14.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:f0a50da861a7ec4573b7c716b2ebdcdf142b66b756a0d392c236ae568b3a93fb"},
- {file = "scipy-1.14.0-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:94c164a9e2498e68308e6e148646e486d979f7fcdb8b4cf34b5441894bdb9caf"},
- {file = "scipy-1.14.0-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:a7d46c3e0aea5c064e734c3eac5cf9eb1f8c4ceee756262f2c7327c4c2691c86"},
- {file = "scipy-1.14.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9eee2989868e274aae26125345584254d97c56194c072ed96cb433f32f692ed8"},
- {file = "scipy-1.14.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e3154691b9f7ed73778d746da2df67a19d046a6c8087c8b385bc4cdb2cfca74"},
- {file = "scipy-1.14.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c40003d880f39c11c1edbae8144e3813904b10514cd3d3d00c277ae996488cdb"},
- {file = "scipy-1.14.0-cp311-cp311-win_amd64.whl", hash = "sha256:5b083c8940028bb7e0b4172acafda6df762da1927b9091f9611b0bcd8676f2bc"},
- {file = "scipy-1.14.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:bff2438ea1330e06e53c424893ec0072640dac00f29c6a43a575cbae4c99b2b9"},
- {file = "scipy-1.14.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:bbc0471b5f22c11c389075d091d3885693fd3f5e9a54ce051b46308bc787e5d4"},
- {file = "scipy-1.14.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:64b2ff514a98cf2bb734a9f90d32dc89dc6ad4a4a36a312cd0d6327170339eb0"},
- {file = "scipy-1.14.0-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:7d3da42fbbbb860211a811782504f38ae7aaec9de8764a9bef6b262de7a2b50f"},
- {file = "scipy-1.14.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d91db2c41dd6c20646af280355d41dfa1ec7eead235642178bd57635a3f82209"},
- {file = "scipy-1.14.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a01cc03bcdc777c9da3cfdcc74b5a75caffb48a6c39c8450a9a05f82c4250a14"},
- {file = "scipy-1.14.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:65df4da3c12a2bb9ad52b86b4dcf46813e869afb006e58be0f516bc370165159"},
- {file = "scipy-1.14.0-cp312-cp312-win_amd64.whl", hash = "sha256:4c4161597c75043f7154238ef419c29a64ac4a7c889d588ea77690ac4d0d9b20"},
- {file = "scipy-1.14.0.tar.gz", hash = "sha256:b5923f48cb840380f9854339176ef21763118a7300a88203ccd0bdd26e58527b"},
-]
-
-[package.dependencies]
-numpy = ">=1.23.5,<2.3"
-
-[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 = ["jupyterlite-pyodide-kernel", "jupyterlite-sphinx (>=0.13.1)", "jupytext", "matplotlib (>=3.5)", "myst-nb", "numpydoc", "pooch", "pydata-sphinx-theme (>=0.15.2)", "sphinx (>=5.0.0)", "sphinx-design (>=0.4.0)"]
-test = ["Cython", "array-api-strict", "asv", "gmpy2", "hypothesis (>=6.30)", "meson", "mpmath", "ninja", "pooch", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "scikit-umfpack", "threadpoolctl"]
-
-[[package]]
-name = "threadpoolctl"
-version = "3.5.0"
-description = "threadpoolctl"
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "threadpoolctl-3.5.0-py3-none-any.whl", hash = "sha256:56c1e26c150397e58c4926da8eeee87533b1e32bef131bd4bf6a2f45f3185467"},
- {file = "threadpoolctl-3.5.0.tar.gz", hash = "sha256:082433502dd922bf738de0d8bcc4fdcbf0979ff44c42bd40f5af8a282f6fa107"},
-]
-
-[metadata]
-lock-version = "2.0"
-python-versions = ">=3.10,<3.13"
-content-hash = "bc0313fa0d96c0dfdf01e69a24501c1d863f6dbe4c0f7e33a8c71dcd3d01d2bd"
diff --git a/pyproject.toml b/pyproject.toml
index 758c163c..08d968ee 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,24 +1,37 @@
-[tool.poetry]
+[project]
name = "useful-optimizer"
version = "0.1.2"
description = "A dedicated set of optimization algorithms for numeric problems."
-authors = ["Anselm Hahn "]
-license = "MIT"
+authors = [{ name = "Anselm Hahn", email = "Anselm.Hahn@gmail.com" }]
+license = { text = "MIT" }
readme = "README.md"
-packages = [{ include = "opt", from = "." }]
-
-[tool.poetry.dependencies]
-python = ">=3.10,<3.13"
-numpy = "^1.26.4"
-scipy = "^1.12.0"
-scikit-learn = "^1.5.1"
+requires-python = ">=3.10,<3.13"
+dependencies = [
+ "numpy>=1.26.4",
+ "scipy>=1.12.0",
+ "scikit-learn>=1.5.1",
+]
-[tool.poetry.group.dev.dependencies]
-ruff = "^0.9.0"
+[project.optional-dependencies]
+dev = [
+ "ruff>=0.9.0",
+ "pytest>=8.0.0",
+ "pre-commit>=4.0.0",
+]
[build-system]
-requires = ["poetry-core"]
-build-backend = "poetry.core.masonry.api"
+requires = ["hatchling"]
+build-backend = "hatchling.build"
+
+[tool.hatch.build.targets.wheel]
+packages = ["opt"]
+
+[dependency-groups]
+dev = [
+ "ruff>=0.9.0",
+ "pytest>=8.0.0",
+ "pre-commit>=4.0.0",
+]
[tool.ruff]
@@ -68,7 +81,7 @@ preview = false
# McCabe complexity (`C901`) by default.
# Set to all
select = ["ALL"]
-ignore = ["ANN101", "PLR0913", "N803", "N806", "E741", "E501", "T201", "COM812"]
+ignore = ["PLR0913", "PLR1704", "N803", "N806", "E741", "E501", "T201", "COM812"]
# Allow fix for all enabled rules (when `--fix`) is provided.
fixable = ["ALL"]
@@ -77,6 +90,9 @@ exclude = [".venv"]
# Allow unused variables when underscore-prefixed.
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
+[tool.ruff.lint.per-file-ignores]
+"opt/test/*" = ["S101", "PLR2004", "PLC0415"]
+
[tool.ruff.lint.pydocstyle]
diff --git a/uv.lock b/uv.lock
new file mode 100644
index 00000000..bf2f936e
--- /dev/null
+++ b/uv.lock
@@ -0,0 +1,547 @@
+version = 1
+revision = 3
+requires-python = ">=3.10, <3.13"
+resolution-markers = [
+ "python_full_version >= '3.11'",
+ "python_full_version < '3.11'",
+]
+
+[[package]]
+name = "cfgv"
+version = "3.5.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/4e/b5/721b8799b04bf9afe054a3899c6cf4e880fcf8563cc71c15610242490a0c/cfgv-3.5.0.tar.gz", hash = "sha256:d5b1034354820651caa73ede66a6294d6e95c1b00acc5e9b098e917404669132", size = 7334, upload-time = "2025-11-19T20:55:51.612Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl", hash = "sha256:a8dc6b26ad22ff227d2634a65cb388215ce6cc96bbcc5cfde7641ae87e8dacc0", size = 7445, upload-time = "2025-11-19T20:55:50.744Z" },
+]
+
+[[package]]
+name = "colorama"
+version = "0.4.6"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
+]
+
+[[package]]
+name = "distlib"
+version = "0.4.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/96/8e/709914eb2b5749865801041647dc7f4e6d00b549cfe88b65ca192995f07c/distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d", size = 614605, upload-time = "2025-07-17T16:52:00.465Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047, upload-time = "2025-07-17T16:51:58.613Z" },
+]
+
+[[package]]
+name = "exceptiongroup"
+version = "1.3.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "typing-extensions", marker = "python_full_version < '3.11'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" },
+]
+
+[[package]]
+name = "filelock"
+version = "3.20.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/a7/23/ce7a1126827cedeb958fc043d61745754464eb56c5937c35bbf2b8e26f34/filelock-3.20.1.tar.gz", hash = "sha256:b8360948b351b80f420878d8516519a2204b07aefcdcfd24912a5d33127f188c", size = 19476, upload-time = "2025-12-15T23:54:28.027Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e3/7f/a1a97644e39e7316d850784c642093c99df1290a460df4ede27659056834/filelock-3.20.1-py3-none-any.whl", hash = "sha256:15d9e9a67306188a44baa72f569d2bfd803076269365fdea0934385da4dc361a", size = 16666, upload-time = "2025-12-15T23:54:26.874Z" },
+]
+
+[[package]]
+name = "identify"
+version = "2.6.15"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/ff/e7/685de97986c916a6d93b3876139e00eef26ad5bbbd61925d670ae8013449/identify-2.6.15.tar.gz", hash = "sha256:e4f4864b96c6557ef2a1e1c951771838f4edc9df3a72ec7118b338801b11c7bf", size = 99311, upload-time = "2025-10-02T17:43:40.631Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/0f/1c/e5fd8f973d4f375adb21565739498e2e9a1e54c858a97b9a8ccfdc81da9b/identify-2.6.15-py2.py3-none-any.whl", hash = "sha256:1181ef7608e00704db228516541eb83a88a9f94433a8c80bb9b5bd54b1d81757", size = 99183, upload-time = "2025-10-02T17:43:39.137Z" },
+]
+
+[[package]]
+name = "iniconfig"
+version = "2.3.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" },
+]
+
+[[package]]
+name = "joblib"
+version = "1.5.3"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/41/f2/d34e8b3a08a9cc79a50b2208a93dce981fe615b64d5a4d4abee421d898df/joblib-1.5.3.tar.gz", hash = "sha256:8561a3269e6801106863fd0d6d84bb737be9e7631e33aaed3fb9ce5953688da3", size = 331603, upload-time = "2025-12-15T08:41:46.427Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/7b/91/984aca2ec129e2757d1e4e3c81c3fcda9d0f85b74670a094cc443d9ee949/joblib-1.5.3-py3-none-any.whl", hash = "sha256:5fc3c5039fc5ca8c0276333a188bbd59d6b7ab37fe6632daa76bc7f9ec18e713", size = 309071, upload-time = "2025-12-15T08:41:44.973Z" },
+]
+
+[[package]]
+name = "nodeenv"
+version = "1.10.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/24/bf/d1bda4f6168e0b2e9e5958945e01910052158313224ada5ce1fb2e1113b8/nodeenv-1.10.0.tar.gz", hash = "sha256:996c191ad80897d076bdfba80a41994c2b47c68e224c542b48feba42ba00f8bb", size = 55611, upload-time = "2025-12-20T14:08:54.006Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl", hash = "sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827", size = 23438, upload-time = "2025-12-20T14:08:52.782Z" },
+]
+
+[[package]]
+name = "numpy"
+version = "2.2.6"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version < '3.11'",
+]
+sdist = { url = "https://files.pythonhosted.org/packages/76/21/7d2a95e4bba9dc13d043ee156a356c0a8f0c6309dff6b21b4d71a073b8a8/numpy-2.2.6.tar.gz", hash = "sha256:e29554e2bef54a90aa5cc07da6ce955accb83f21ab5de01a62c8478897b264fd", size = 20276440, upload-time = "2025-05-17T22:38:04.611Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/9a/3e/ed6db5be21ce87955c0cbd3009f2803f59fa08df21b5df06862e2d8e2bdd/numpy-2.2.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b412caa66f72040e6d268491a59f2c43bf03eb6c96dd8f0307829feb7fa2b6fb", size = 21165245, upload-time = "2025-05-17T21:27:58.555Z" },
+ { url = "https://files.pythonhosted.org/packages/22/c2/4b9221495b2a132cc9d2eb862e21d42a009f5a60e45fc44b00118c174bff/numpy-2.2.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e41fd67c52b86603a91c1a505ebaef50b3314de0213461c7a6e99c9a3beff90", size = 14360048, upload-time = "2025-05-17T21:28:21.406Z" },
+ { url = "https://files.pythonhosted.org/packages/fd/77/dc2fcfc66943c6410e2bf598062f5959372735ffda175b39906d54f02349/numpy-2.2.6-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:37e990a01ae6ec7fe7fa1c26c55ecb672dd98b19c3d0e1d1f326fa13cb38d163", size = 5340542, upload-time = "2025-05-17T21:28:30.931Z" },
+ { url = "https://files.pythonhosted.org/packages/7a/4f/1cb5fdc353a5f5cc7feb692db9b8ec2c3d6405453f982435efc52561df58/numpy-2.2.6-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:5a6429d4be8ca66d889b7cf70f536a397dc45ba6faeb5f8c5427935d9592e9cf", size = 6878301, upload-time = "2025-05-17T21:28:41.613Z" },
+ { url = "https://files.pythonhosted.org/packages/eb/17/96a3acd228cec142fcb8723bd3cc39c2a474f7dcf0a5d16731980bcafa95/numpy-2.2.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:efd28d4e9cd7d7a8d39074a4d44c63eda73401580c5c76acda2ce969e0a38e83", size = 14297320, upload-time = "2025-05-17T21:29:02.78Z" },
+ { url = "https://files.pythonhosted.org/packages/b4/63/3de6a34ad7ad6646ac7d2f55ebc6ad439dbbf9c4370017c50cf403fb19b5/numpy-2.2.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc7b73d02efb0e18c000e9ad8b83480dfcd5dfd11065997ed4c6747470ae8915", size = 16801050, upload-time = "2025-05-17T21:29:27.675Z" },
+ { url = "https://files.pythonhosted.org/packages/07/b6/89d837eddef52b3d0cec5c6ba0456c1bf1b9ef6a6672fc2b7873c3ec4e2e/numpy-2.2.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:74d4531beb257d2c3f4b261bfb0fc09e0f9ebb8842d82a7b4209415896adc680", size = 15807034, upload-time = "2025-05-17T21:29:51.102Z" },
+ { url = "https://files.pythonhosted.org/packages/01/c8/dc6ae86e3c61cfec1f178e5c9f7858584049b6093f843bca541f94120920/numpy-2.2.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8fc377d995680230e83241d8a96def29f204b5782f371c532579b4f20607a289", size = 18614185, upload-time = "2025-05-17T21:30:18.703Z" },
+ { url = "https://files.pythonhosted.org/packages/5b/c5/0064b1b7e7c89137b471ccec1fd2282fceaae0ab3a9550f2568782d80357/numpy-2.2.6-cp310-cp310-win32.whl", hash = "sha256:b093dd74e50a8cba3e873868d9e93a85b78e0daf2e98c6797566ad8044e8363d", size = 6527149, upload-time = "2025-05-17T21:30:29.788Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/dd/4b822569d6b96c39d1215dbae0582fd99954dcbcf0c1a13c61783feaca3f/numpy-2.2.6-cp310-cp310-win_amd64.whl", hash = "sha256:f0fd6321b839904e15c46e0d257fdd101dd7f530fe03fd6359c1ea63738703f3", size = 12904620, upload-time = "2025-05-17T21:30:48.994Z" },
+ { url = "https://files.pythonhosted.org/packages/da/a8/4f83e2aa666a9fbf56d6118faaaf5f1974d456b1823fda0a176eff722839/numpy-2.2.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f9f1adb22318e121c5c69a09142811a201ef17ab257a1e66ca3025065b7f53ae", size = 21176963, upload-time = "2025-05-17T21:31:19.36Z" },
+ { url = "https://files.pythonhosted.org/packages/b3/2b/64e1affc7972decb74c9e29e5649fac940514910960ba25cd9af4488b66c/numpy-2.2.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c820a93b0255bc360f53eca31a0e676fd1101f673dda8da93454a12e23fc5f7a", size = 14406743, upload-time = "2025-05-17T21:31:41.087Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/9f/0121e375000b5e50ffdd8b25bf78d8e1a5aa4cca3f185d41265198c7b834/numpy-2.2.6-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3d70692235e759f260c3d837193090014aebdf026dfd167834bcba43e30c2a42", size = 5352616, upload-time = "2025-05-17T21:31:50.072Z" },
+ { url = "https://files.pythonhosted.org/packages/31/0d/b48c405c91693635fbe2dcd7bc84a33a602add5f63286e024d3b6741411c/numpy-2.2.6-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:481b49095335f8eed42e39e8041327c05b0f6f4780488f61286ed3c01368d491", size = 6889579, upload-time = "2025-05-17T21:32:01.712Z" },
+ { url = "https://files.pythonhosted.org/packages/52/b8/7f0554d49b565d0171eab6e99001846882000883998e7b7d9f0d98b1f934/numpy-2.2.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b64d8d4d17135e00c8e346e0a738deb17e754230d7e0810ac5012750bbd85a5a", size = 14312005, upload-time = "2025-05-17T21:32:23.332Z" },
+ { url = "https://files.pythonhosted.org/packages/b3/dd/2238b898e51bd6d389b7389ffb20d7f4c10066d80351187ec8e303a5a475/numpy-2.2.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba10f8411898fc418a521833e014a77d3ca01c15b0c6cdcce6a0d2897e6dbbdf", size = 16821570, upload-time = "2025-05-17T21:32:47.991Z" },
+ { url = "https://files.pythonhosted.org/packages/83/6c/44d0325722cf644f191042bf47eedad61c1e6df2432ed65cbe28509d404e/numpy-2.2.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bd48227a919f1bafbdda0583705e547892342c26fb127219d60a5c36882609d1", size = 15818548, upload-time = "2025-05-17T21:33:11.728Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/9d/81e8216030ce66be25279098789b665d49ff19eef08bfa8cb96d4957f422/numpy-2.2.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9551a499bf125c1d4f9e250377c1ee2eddd02e01eac6644c080162c0c51778ab", size = 18620521, upload-time = "2025-05-17T21:33:39.139Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/fd/e19617b9530b031db51b0926eed5345ce8ddc669bb3bc0044b23e275ebe8/numpy-2.2.6-cp311-cp311-win32.whl", hash = "sha256:0678000bb9ac1475cd454c6b8c799206af8107e310843532b04d49649c717a47", size = 6525866, upload-time = "2025-05-17T21:33:50.273Z" },
+ { url = "https://files.pythonhosted.org/packages/31/0a/f354fb7176b81747d870f7991dc763e157a934c717b67b58456bc63da3df/numpy-2.2.6-cp311-cp311-win_amd64.whl", hash = "sha256:e8213002e427c69c45a52bbd94163084025f533a55a59d6f9c5b820774ef3303", size = 12907455, upload-time = "2025-05-17T21:34:09.135Z" },
+ { url = "https://files.pythonhosted.org/packages/82/5d/c00588b6cf18e1da539b45d3598d3557084990dcc4331960c15ee776ee41/numpy-2.2.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:41c5a21f4a04fa86436124d388f6ed60a9343a6f767fced1a8a71c3fbca038ff", size = 20875348, upload-time = "2025-05-17T21:34:39.648Z" },
+ { url = "https://files.pythonhosted.org/packages/66/ee/560deadcdde6c2f90200450d5938f63a34b37e27ebff162810f716f6a230/numpy-2.2.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de749064336d37e340f640b05f24e9e3dd678c57318c7289d222a8a2f543e90c", size = 14119362, upload-time = "2025-05-17T21:35:01.241Z" },
+ { url = "https://files.pythonhosted.org/packages/3c/65/4baa99f1c53b30adf0acd9a5519078871ddde8d2339dc5a7fde80d9d87da/numpy-2.2.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:894b3a42502226a1cac872f840030665f33326fc3dac8e57c607905773cdcde3", size = 5084103, upload-time = "2025-05-17T21:35:10.622Z" },
+ { url = "https://files.pythonhosted.org/packages/cc/89/e5a34c071a0570cc40c9a54eb472d113eea6d002e9ae12bb3a8407fb912e/numpy-2.2.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:71594f7c51a18e728451bb50cc60a3ce4e6538822731b2933209a1f3614e9282", size = 6625382, upload-time = "2025-05-17T21:35:21.414Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/35/8c80729f1ff76b3921d5c9487c7ac3de9b2a103b1cd05e905b3090513510/numpy-2.2.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2618db89be1b4e05f7a1a847a9c1c0abd63e63a1607d892dd54668dd92faf87", size = 14018462, upload-time = "2025-05-17T21:35:42.174Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/3d/1e1db36cfd41f895d266b103df00ca5b3cbe965184df824dec5c08c6b803/numpy-2.2.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd83c01228a688733f1ded5201c678f0c53ecc1006ffbc404db9f7a899ac6249", size = 16527618, upload-time = "2025-05-17T21:36:06.711Z" },
+ { url = "https://files.pythonhosted.org/packages/61/c6/03ed30992602c85aa3cd95b9070a514f8b3c33e31124694438d88809ae36/numpy-2.2.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:37c0ca431f82cd5fa716eca9506aefcabc247fb27ba69c5062a6d3ade8cf8f49", size = 15505511, upload-time = "2025-05-17T21:36:29.965Z" },
+ { url = "https://files.pythonhosted.org/packages/b7/25/5761d832a81df431e260719ec45de696414266613c9ee268394dd5ad8236/numpy-2.2.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fe27749d33bb772c80dcd84ae7e8df2adc920ae8297400dabec45f0dedb3f6de", size = 18313783, upload-time = "2025-05-17T21:36:56.883Z" },
+ { url = "https://files.pythonhosted.org/packages/57/0a/72d5a3527c5ebffcd47bde9162c39fae1f90138c961e5296491ce778e682/numpy-2.2.6-cp312-cp312-win32.whl", hash = "sha256:4eeaae00d789f66c7a25ac5f34b71a7035bb474e679f410e5e1a94deb24cf2d4", size = 6246506, upload-time = "2025-05-17T21:37:07.368Z" },
+ { url = "https://files.pythonhosted.org/packages/36/fa/8c9210162ca1b88529ab76b41ba02d433fd54fecaf6feb70ef9f124683f1/numpy-2.2.6-cp312-cp312-win_amd64.whl", hash = "sha256:c1f9540be57940698ed329904db803cf7a402f3fc200bfe599334c9bd84a40b2", size = 12614190, upload-time = "2025-05-17T21:37:26.213Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/3b/d94a75f4dbf1ef5d321523ecac21ef23a3cd2ac8b78ae2aac40873590229/numpy-2.2.6-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0b605b275d7bd0c640cad4e5d30fa701a8d59302e127e5f79138ad62762c3e3d", size = 21040391, upload-time = "2025-05-17T21:44:35.948Z" },
+ { url = "https://files.pythonhosted.org/packages/17/f4/09b2fa1b58f0fb4f7c7963a1649c64c4d315752240377ed74d9cd878f7b5/numpy-2.2.6-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:7befc596a7dc9da8a337f79802ee8adb30a552a94f792b9c9d18c840055907db", size = 6786754, upload-time = "2025-05-17T21:44:47.446Z" },
+ { url = "https://files.pythonhosted.org/packages/af/30/feba75f143bdc868a1cc3f44ccfa6c4b9ec522b36458e738cd00f67b573f/numpy-2.2.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce47521a4754c8f4593837384bd3424880629f718d87c5d44f8ed763edd63543", size = 16643476, upload-time = "2025-05-17T21:45:11.871Z" },
+ { url = "https://files.pythonhosted.org/packages/37/48/ac2a9584402fb6c0cd5b5d1a91dcf176b15760130dd386bbafdbfe3640bf/numpy-2.2.6-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d042d24c90c41b54fd506da306759e06e568864df8ec17ccc17e9e884634fd00", size = 12812666, upload-time = "2025-05-17T21:45:31.426Z" },
+]
+
+[[package]]
+name = "numpy"
+version = "2.3.5"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version >= '3.11'",
+]
+sdist = { url = "https://files.pythonhosted.org/packages/76/65/21b3bc86aac7b8f2862db1e808f1ea22b028e30a225a34a5ede9bf8678f2/numpy-2.3.5.tar.gz", hash = "sha256:784db1dcdab56bf0517743e746dfb0f885fc68d948aba86eeec2cba234bdf1c0", size = 20584950, upload-time = "2025-11-16T22:52:42.067Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/43/77/84dd1d2e34d7e2792a236ba180b5e8fcc1e3e414e761ce0253f63d7f572e/numpy-2.3.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:de5672f4a7b200c15a4127042170a694d4df43c992948f5e1af57f0174beed10", size = 17034641, upload-time = "2025-11-16T22:49:19.336Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/ea/25e26fa5837106cde46ae7d0b667e20f69cbbc0efd64cba8221411ab26ae/numpy-2.3.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:acfd89508504a19ed06ef963ad544ec6664518c863436306153e13e94605c218", size = 12528324, upload-time = "2025-11-16T22:49:22.582Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/1a/e85f0eea4cf03d6a0228f5c0256b53f2df4bc794706e7df019fc622e47f1/numpy-2.3.5-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:ffe22d2b05504f786c867c8395de703937f934272eb67586817b46188b4ded6d", size = 5356872, upload-time = "2025-11-16T22:49:25.408Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/bb/35ef04afd567f4c989c2060cde39211e4ac5357155c1833bcd1166055c61/numpy-2.3.5-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:872a5cf366aec6bb1147336480fef14c9164b154aeb6542327de4970282cd2f5", size = 6893148, upload-time = "2025-11-16T22:49:27.549Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/2b/05bbeb06e2dff5eab512dfc678b1cc5ee94d8ac5956a0885c64b6b26252b/numpy-2.3.5-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3095bdb8dd297e5920b010e96134ed91d852d81d490e787beca7e35ae1d89cf7", size = 14557282, upload-time = "2025-11-16T22:49:30.964Z" },
+ { url = "https://files.pythonhosted.org/packages/65/fb/2b23769462b34398d9326081fad5655198fcf18966fcb1f1e49db44fbf31/numpy-2.3.5-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8cba086a43d54ca804ce711b2a940b16e452807acebe7852ff327f1ecd49b0d4", size = 16897903, upload-time = "2025-11-16T22:49:34.191Z" },
+ { url = "https://files.pythonhosted.org/packages/ac/14/085f4cf05fc3f1e8aa95e85404e984ffca9b2275a5dc2b1aae18a67538b8/numpy-2.3.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6cf9b429b21df6b99f4dee7a1218b8b7ffbbe7df8764dc0bd60ce8a0708fed1e", size = 16341672, upload-time = "2025-11-16T22:49:37.2Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/3b/1f73994904142b2aa290449b3bb99772477b5fd94d787093e4f24f5af763/numpy-2.3.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:396084a36abdb603546b119d96528c2f6263921c50df3c8fd7cb28873a237748", size = 18838896, upload-time = "2025-11-16T22:49:39.727Z" },
+ { url = "https://files.pythonhosted.org/packages/cd/b9/cf6649b2124f288309ffc353070792caf42ad69047dcc60da85ee85fea58/numpy-2.3.5-cp311-cp311-win32.whl", hash = "sha256:b0c7088a73aef3d687c4deef8452a3ac7c1be4e29ed8bf3b366c8111128ac60c", size = 6563608, upload-time = "2025-11-16T22:49:42.079Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/44/9fe81ae1dcc29c531843852e2874080dc441338574ccc4306b39e2ff6e59/numpy-2.3.5-cp311-cp311-win_amd64.whl", hash = "sha256:a414504bef8945eae5f2d7cb7be2d4af77c5d1cb5e20b296c2c25b61dff2900c", size = 13078442, upload-time = "2025-11-16T22:49:43.99Z" },
+ { url = "https://files.pythonhosted.org/packages/6d/a7/f99a41553d2da82a20a2f22e93c94f928e4490bb447c9ff3c4ff230581d3/numpy-2.3.5-cp311-cp311-win_arm64.whl", hash = "sha256:0cd00b7b36e35398fa2d16af7b907b65304ef8bb4817a550e06e5012929830fa", size = 10458555, upload-time = "2025-11-16T22:49:47.092Z" },
+ { url = "https://files.pythonhosted.org/packages/44/37/e669fe6cbb2b96c62f6bbedc6a81c0f3b7362f6a59230b23caa673a85721/numpy-2.3.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:74ae7b798248fe62021dbf3c914245ad45d1a6b0cb4a29ecb4b31d0bfbc4cc3e", size = 16733873, upload-time = "2025-11-16T22:49:49.84Z" },
+ { url = "https://files.pythonhosted.org/packages/c5/65/df0db6c097892c9380851ab9e44b52d4f7ba576b833996e0080181c0c439/numpy-2.3.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ee3888d9ff7c14604052b2ca5535a30216aa0a58e948cdd3eeb8d3415f638769", size = 12259838, upload-time = "2025-11-16T22:49:52.863Z" },
+ { url = "https://files.pythonhosted.org/packages/5b/e1/1ee06e70eb2136797abe847d386e7c0e830b67ad1d43f364dd04fa50d338/numpy-2.3.5-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:612a95a17655e213502f60cfb9bf9408efdc9eb1d5f50535cc6eb365d11b42b5", size = 5088378, upload-time = "2025-11-16T22:49:55.055Z" },
+ { url = "https://files.pythonhosted.org/packages/6d/9c/1ca85fb86708724275103b81ec4cf1ac1d08f465368acfc8da7ab545bdae/numpy-2.3.5-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:3101e5177d114a593d79dd79658650fe28b5a0d8abeb8ce6f437c0e6df5be1a4", size = 6628559, upload-time = "2025-11-16T22:49:57.371Z" },
+ { url = "https://files.pythonhosted.org/packages/74/78/fcd41e5a0ce4f3f7b003da85825acddae6d7ecb60cf25194741b036ca7d6/numpy-2.3.5-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b973c57ff8e184109db042c842423ff4f60446239bd585a5131cc47f06f789d", size = 14250702, upload-time = "2025-11-16T22:49:59.632Z" },
+ { url = "https://files.pythonhosted.org/packages/b6/23/2a1b231b8ff672b4c450dac27164a8b2ca7d9b7144f9c02d2396518352eb/numpy-2.3.5-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0d8163f43acde9a73c2a33605353a4f1bc4798745a8b1d73183b28e5b435ae28", size = 16606086, upload-time = "2025-11-16T22:50:02.127Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/c5/5ad26fbfbe2012e190cc7d5003e4d874b88bb18861d0829edc140a713021/numpy-2.3.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:51c1e14eb1e154ebd80e860722f9e6ed6ec89714ad2db2d3aa33c31d7c12179b", size = 16025985, upload-time = "2025-11-16T22:50:04.536Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/fa/dd48e225c46c819288148d9d060b047fd2a6fb1eb37eae25112ee4cb4453/numpy-2.3.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b46b4ec24f7293f23adcd2d146960559aaf8020213de8ad1909dba6c013bf89c", size = 18542976, upload-time = "2025-11-16T22:50:07.557Z" },
+ { url = "https://files.pythonhosted.org/packages/05/79/ccbd23a75862d95af03d28b5c6901a1b7da4803181513d52f3b86ed9446e/numpy-2.3.5-cp312-cp312-win32.whl", hash = "sha256:3997b5b3c9a771e157f9aae01dd579ee35ad7109be18db0e85dbdbe1de06e952", size = 6285274, upload-time = "2025-11-16T22:50:10.746Z" },
+ { url = "https://files.pythonhosted.org/packages/2d/57/8aeaf160312f7f489dea47ab61e430b5cb051f59a98ae68b7133ce8fa06a/numpy-2.3.5-cp312-cp312-win_amd64.whl", hash = "sha256:86945f2ee6d10cdfd67bcb4069c1662dd711f7e2a4343db5cecec06b87cf31aa", size = 12782922, upload-time = "2025-11-16T22:50:12.811Z" },
+ { url = "https://files.pythonhosted.org/packages/78/a6/aae5cc2ca78c45e64b9ef22f089141d661516856cf7c8a54ba434576900d/numpy-2.3.5-cp312-cp312-win_arm64.whl", hash = "sha256:f28620fe26bee16243be2b7b874da327312240a7cdc38b769a697578d2100013", size = 10194667, upload-time = "2025-11-16T22:50:16.16Z" },
+ { url = "https://files.pythonhosted.org/packages/c6/65/f9dea8e109371ade9c782b4e4756a82edf9d3366bca495d84d79859a0b79/numpy-2.3.5-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:f0963b55cdd70fad460fa4c1341f12f976bb26cb66021a5580329bd498988310", size = 16910689, upload-time = "2025-11-16T22:52:23.247Z" },
+ { url = "https://files.pythonhosted.org/packages/00/4f/edb00032a8fb92ec0a679d3830368355da91a69cab6f3e9c21b64d0bb986/numpy-2.3.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:f4255143f5160d0de972d28c8f9665d882b5f61309d8362fdd3e103cf7bf010c", size = 12457053, upload-time = "2025-11-16T22:52:26.367Z" },
+ { url = "https://files.pythonhosted.org/packages/16/a4/e8a53b5abd500a63836a29ebe145fc1ab1f2eefe1cfe59276020373ae0aa/numpy-2.3.5-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:a4b9159734b326535f4dd01d947f919c6eefd2d9827466a696c44ced82dfbc18", size = 5285635, upload-time = "2025-11-16T22:52:29.266Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/2f/37eeb9014d9c8b3e9c55bc599c68263ca44fdbc12a93e45a21d1d56df737/numpy-2.3.5-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:2feae0d2c91d46e59fcd62784a3a83b3fb677fead592ce51b5a6fbb4f95965ff", size = 6801770, upload-time = "2025-11-16T22:52:31.421Z" },
+ { url = "https://files.pythonhosted.org/packages/7d/e4/68d2f474df2cb671b2b6c2986a02e520671295647dad82484cde80ca427b/numpy-2.3.5-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ffac52f28a7849ad7576293c0cb7b9f08304e8f7d738a8cb8a90ec4c55a998eb", size = 14391768, upload-time = "2025-11-16T22:52:33.593Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/50/94ccd8a2b141cb50651fddd4f6a48874acb3c91c8f0842b08a6afc4b0b21/numpy-2.3.5-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:63c0e9e7eea69588479ebf4a8a270d5ac22763cc5854e9a7eae952a3908103f7", size = 16729263, upload-time = "2025-11-16T22:52:36.369Z" },
+ { url = "https://files.pythonhosted.org/packages/2d/ee/346fa473e666fe14c52fcdd19ec2424157290a032d4c41f98127bfb31ac7/numpy-2.3.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:f16417ec91f12f814b10bafe79ef77e70113a2f5f7018640e7425ff979253425", size = 12967213, upload-time = "2025-11-16T22:52:39.38Z" },
+]
+
+[[package]]
+name = "packaging"
+version = "25.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" },
+]
+
+[[package]]
+name = "platformdirs"
+version = "4.5.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/cf/86/0248f086a84f01b37aaec0fa567b397df1a119f73c16f6c7a9aac73ea309/platformdirs-4.5.1.tar.gz", hash = "sha256:61d5cdcc6065745cdd94f0f878977f8de9437be93de97c1c12f853c9c0cdcbda", size = 21715, upload-time = "2025-12-05T13:52:58.638Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl", hash = "sha256:d03afa3963c806a9bed9d5125c8f4cb2fdaf74a55ab60e5d59b3fde758104d31", size = 18731, upload-time = "2025-12-05T13:52:56.823Z" },
+]
+
+[[package]]
+name = "pluggy"
+version = "1.6.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" },
+]
+
+[[package]]
+name = "pre-commit"
+version = "4.5.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "cfgv" },
+ { name = "identify" },
+ { name = "nodeenv" },
+ { name = "pyyaml" },
+ { name = "virtualenv" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/40/f1/6d86a29246dfd2e9b6237f0b5823717f60cad94d47ddc26afa916d21f525/pre_commit-4.5.1.tar.gz", hash = "sha256:eb545fcff725875197837263e977ea257a402056661f09dae08e4b149b030a61", size = 198232, upload-time = "2025-12-16T21:14:33.552Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/5d/19/fd3ef348460c80af7bb4669ea7926651d1f95c23ff2df18b9d24bab4f3fa/pre_commit-4.5.1-py2.py3-none-any.whl", hash = "sha256:3b3afd891e97337708c1674210f8eba659b52a38ea5f822ff142d10786221f77", size = 226437, upload-time = "2025-12-16T21:14:32.409Z" },
+]
+
+[[package]]
+name = "pygments"
+version = "2.19.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" },
+]
+
+[[package]]
+name = "pytest"
+version = "9.0.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "colorama", marker = "sys_platform == 'win32'" },
+ { name = "exceptiongroup", marker = "python_full_version < '3.11'" },
+ { name = "iniconfig" },
+ { name = "packaging" },
+ { name = "pluggy" },
+ { name = "pygments" },
+ { name = "tomli", marker = "python_full_version < '3.11'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/d1/db/7ef3487e0fb0049ddb5ce41d3a49c235bf9ad299b6a25d5780a89f19230f/pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11", size = 1568901, upload-time = "2025-12-06T21:30:51.014Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b", size = 374801, upload-time = "2025-12-06T21:30:49.154Z" },
+]
+
+[[package]]
+name = "pyyaml"
+version = "6.0.3"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/f4/a0/39350dd17dd6d6c6507025c0e53aef67a9293a6d37d3511f23ea510d5800/pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b", size = 184227, upload-time = "2025-09-25T21:31:46.04Z" },
+ { url = "https://files.pythonhosted.org/packages/05/14/52d505b5c59ce73244f59c7a50ecf47093ce4765f116cdb98286a71eeca2/pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956", size = 174019, upload-time = "2025-09-25T21:31:47.706Z" },
+ { url = "https://files.pythonhosted.org/packages/43/f7/0e6a5ae5599c838c696adb4e6330a59f463265bfa1e116cfd1fbb0abaaae/pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8", size = 740646, upload-time = "2025-09-25T21:31:49.21Z" },
+ { url = "https://files.pythonhosted.org/packages/2f/3a/61b9db1d28f00f8fd0ae760459a5c4bf1b941baf714e207b6eb0657d2578/pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198", size = 840793, upload-time = "2025-09-25T21:31:50.735Z" },
+ { url = "https://files.pythonhosted.org/packages/7a/1e/7acc4f0e74c4b3d9531e24739e0ab832a5edf40e64fbae1a9c01941cabd7/pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b", size = 770293, upload-time = "2025-09-25T21:31:51.828Z" },
+ { url = "https://files.pythonhosted.org/packages/8b/ef/abd085f06853af0cd59fa5f913d61a8eab65d7639ff2a658d18a25d6a89d/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0", size = 732872, upload-time = "2025-09-25T21:31:53.282Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/15/2bc9c8faf6450a8b3c9fc5448ed869c599c0a74ba2669772b1f3a0040180/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69", size = 758828, upload-time = "2025-09-25T21:31:54.807Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/00/531e92e88c00f4333ce359e50c19b8d1de9fe8d581b1534e35ccfbc5f393/pyyaml-6.0.3-cp310-cp310-win32.whl", hash = "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e", size = 142415, upload-time = "2025-09-25T21:31:55.885Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/fa/926c003379b19fca39dd4634818b00dec6c62d87faf628d1394e137354d4/pyyaml-6.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c", size = 158561, upload-time = "2025-09-25T21:31:57.406Z" },
+ { url = "https://files.pythonhosted.org/packages/6d/16/a95b6757765b7b031c9374925bb718d55e0a9ba8a1b6a12d25962ea44347/pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e", size = 185826, upload-time = "2025-09-25T21:31:58.655Z" },
+ { url = "https://files.pythonhosted.org/packages/16/19/13de8e4377ed53079ee996e1ab0a9c33ec2faf808a4647b7b4c0d46dd239/pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824", size = 175577, upload-time = "2025-09-25T21:32:00.088Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/62/d2eb46264d4b157dae1275b573017abec435397aa59cbcdab6fc978a8af4/pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c", size = 775556, upload-time = "2025-09-25T21:32:01.31Z" },
+ { url = "https://files.pythonhosted.org/packages/10/cb/16c3f2cf3266edd25aaa00d6c4350381c8b012ed6f5276675b9eba8d9ff4/pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00", size = 882114, upload-time = "2025-09-25T21:32:03.376Z" },
+ { url = "https://files.pythonhosted.org/packages/71/60/917329f640924b18ff085ab889a11c763e0b573da888e8404ff486657602/pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d", size = 806638, upload-time = "2025-09-25T21:32:04.553Z" },
+ { url = "https://files.pythonhosted.org/packages/dd/6f/529b0f316a9fd167281a6c3826b5583e6192dba792dd55e3203d3f8e655a/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a", size = 767463, upload-time = "2025-09-25T21:32:06.152Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/6a/b627b4e0c1dd03718543519ffb2f1deea4a1e6d42fbab8021936a4d22589/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4", size = 794986, upload-time = "2025-09-25T21:32:07.367Z" },
+ { url = "https://files.pythonhosted.org/packages/45/91/47a6e1c42d9ee337c4839208f30d9f09caa9f720ec7582917b264defc875/pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b", size = 142543, upload-time = "2025-09-25T21:32:08.95Z" },
+ { url = "https://files.pythonhosted.org/packages/da/e3/ea007450a105ae919a72393cb06f122f288ef60bba2dc64b26e2646fa315/pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf", size = 158763, upload-time = "2025-09-25T21:32:09.96Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" },
+ { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" },
+ { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" },
+ { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" },
+ { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" },
+ { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" },
+ { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" },
+ { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" },
+ { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" },
+]
+
+[[package]]
+name = "ruff"
+version = "0.14.10"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/57/08/52232a877978dd8f9cf2aeddce3e611b40a63287dfca29b6b8da791f5e8d/ruff-0.14.10.tar.gz", hash = "sha256:9a2e830f075d1a42cd28420d7809ace390832a490ed0966fe373ba288e77aaf4", size = 5859763, upload-time = "2025-12-18T19:28:57.98Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/60/01/933704d69f3f05ee16ef11406b78881733c186fe14b6a46b05cfcaf6d3b2/ruff-0.14.10-py3-none-linux_armv6l.whl", hash = "sha256:7a3ce585f2ade3e1f29ec1b92df13e3da262178df8c8bdf876f48fa0e8316c49", size = 13527080, upload-time = "2025-12-18T19:29:25.642Z" },
+ { url = "https://files.pythonhosted.org/packages/df/58/a0349197a7dfa603ffb7f5b0470391efa79ddc327c1e29c4851e85b09cc5/ruff-0.14.10-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:674f9be9372907f7257c51f1d4fc902cb7cf014b9980152b802794317941f08f", size = 13797320, upload-time = "2025-12-18T19:29:02.571Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/82/36be59f00a6082e38c23536df4e71cdbc6af8d7c707eade97fcad5c98235/ruff-0.14.10-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d85713d522348837ef9df8efca33ccb8bd6fcfc86a2cde3ccb4bc9d28a18003d", size = 12918434, upload-time = "2025-12-18T19:28:51.202Z" },
+ { url = "https://files.pythonhosted.org/packages/a6/00/45c62a7f7e34da92a25804f813ebe05c88aa9e0c25e5cb5a7d23dd7450e3/ruff-0.14.10-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6987ebe0501ae4f4308d7d24e2d0fe3d7a98430f5adfd0f1fead050a740a3a77", size = 13371961, upload-time = "2025-12-18T19:29:04.991Z" },
+ { url = "https://files.pythonhosted.org/packages/40/31/a5906d60f0405f7e57045a70f2d57084a93ca7425f22e1d66904769d1628/ruff-0.14.10-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:16a01dfb7b9e4eee556fbfd5392806b1b8550c9b4a9f6acd3dbe6812b193c70a", size = 13275629, upload-time = "2025-12-18T19:29:21.381Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/60/61c0087df21894cf9d928dc04bcd4fb10e8b2e8dca7b1a276ba2155b2002/ruff-0.14.10-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7165d31a925b7a294465fa81be8c12a0e9b60fb02bf177e79067c867e71f8b1f", size = 14029234, upload-time = "2025-12-18T19:29:00.132Z" },
+ { url = "https://files.pythonhosted.org/packages/44/84/77d911bee3b92348b6e5dab5a0c898d87084ea03ac5dc708f46d88407def/ruff-0.14.10-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:c561695675b972effb0c0a45db233f2c816ff3da8dcfbe7dfc7eed625f218935", size = 15449890, upload-time = "2025-12-18T19:28:53.573Z" },
+ { url = "https://files.pythonhosted.org/packages/e9/36/480206eaefa24a7ec321582dda580443a8f0671fdbf6b1c80e9c3e93a16a/ruff-0.14.10-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4bb98fcbbc61725968893682fd4df8966a34611239c9fd07a1f6a07e7103d08e", size = 15123172, upload-time = "2025-12-18T19:29:23.453Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/38/68e414156015ba80cef5473d57919d27dfb62ec804b96180bafdeaf0e090/ruff-0.14.10-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f24b47993a9d8cb858429e97bdf8544c78029f09b520af615c1d261bf827001d", size = 14460260, upload-time = "2025-12-18T19:29:27.808Z" },
+ { url = "https://files.pythonhosted.org/packages/b3/19/9e050c0dca8aba824d67cc0db69fb459c28d8cd3f6855b1405b3f29cc91d/ruff-0.14.10-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59aabd2e2c4fd614d2862e7939c34a532c04f1084476d6833dddef4afab87e9f", size = 14229978, upload-time = "2025-12-18T19:29:11.32Z" },
+ { url = "https://files.pythonhosted.org/packages/51/eb/e8dd1dd6e05b9e695aa9dd420f4577debdd0f87a5ff2fedda33c09e9be8c/ruff-0.14.10-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:213db2b2e44be8625002dbea33bb9c60c66ea2c07c084a00d55732689d697a7f", size = 14338036, upload-time = "2025-12-18T19:29:09.184Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/12/f3e3a505db7c19303b70af370d137795fcfec136d670d5de5391e295c134/ruff-0.14.10-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:b914c40ab64865a17a9a5b67911d14df72346a634527240039eb3bd650e5979d", size = 13264051, upload-time = "2025-12-18T19:29:13.431Z" },
+ { url = "https://files.pythonhosted.org/packages/08/64/8c3a47eaccfef8ac20e0484e68e0772013eb85802f8a9f7603ca751eb166/ruff-0.14.10-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:1484983559f026788e3a5c07c81ef7d1e97c1c78ed03041a18f75df104c45405", size = 13283998, upload-time = "2025-12-18T19:29:06.994Z" },
+ { url = "https://files.pythonhosted.org/packages/12/84/534a5506f4074e5cc0529e5cd96cfc01bb480e460c7edf5af70d2bcae55e/ruff-0.14.10-py3-none-musllinux_1_2_i686.whl", hash = "sha256:c70427132db492d25f982fffc8d6c7535cc2fd2c83fc8888f05caaa248521e60", size = 13601891, upload-time = "2025-12-18T19:28:55.811Z" },
+ { url = "https://files.pythonhosted.org/packages/0d/1e/14c916087d8598917dbad9b2921d340f7884824ad6e9c55de948a93b106d/ruff-0.14.10-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5bcf45b681e9f1ee6445d317ce1fa9d6cba9a6049542d1c3d5b5958986be8830", size = 14336660, upload-time = "2025-12-18T19:29:16.531Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/1c/d7b67ab43f30013b47c12b42d1acd354c195351a3f7a1d67f59e54227ede/ruff-0.14.10-py3-none-win32.whl", hash = "sha256:104c49fc7ab73f3f3a758039adea978869a918f31b73280db175b43a2d9b51d6", size = 13196187, upload-time = "2025-12-18T19:29:19.006Z" },
+ { url = "https://files.pythonhosted.org/packages/fb/9c/896c862e13886fae2af961bef3e6312db9ebc6adc2b156fe95e615dee8c1/ruff-0.14.10-py3-none-win_amd64.whl", hash = "sha256:466297bd73638c6bdf06485683e812db1c00c7ac96d4ddd0294a338c62fdc154", size = 14661283, upload-time = "2025-12-18T19:29:30.16Z" },
+ { url = "https://files.pythonhosted.org/packages/74/31/b0e29d572670dca3674eeee78e418f20bdf97fa8aa9ea71380885e175ca0/ruff-0.14.10-py3-none-win_arm64.whl", hash = "sha256:e51d046cf6dda98a4633b8a8a771451107413b0f07183b2bef03f075599e44e6", size = 13729839, upload-time = "2025-12-18T19:28:48.636Z" },
+]
+
+[[package]]
+name = "scikit-learn"
+version = "1.7.2"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version < '3.11'",
+]
+dependencies = [
+ { name = "joblib", marker = "python_full_version < '3.11'" },
+ { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
+ { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
+ { name = "threadpoolctl", marker = "python_full_version < '3.11'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/98/c2/a7855e41c9d285dfe86dc50b250978105dce513d6e459ea66a6aeb0e1e0c/scikit_learn-1.7.2.tar.gz", hash = "sha256:20e9e49ecd130598f1ca38a1d85090e1a600147b9c02fa6f15d69cb53d968fda", size = 7193136, upload-time = "2025-09-09T08:21:29.075Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ba/3e/daed796fd69cce768b8788401cc464ea90b306fb196ae1ffed0b98182859/scikit_learn-1.7.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b33579c10a3081d076ab403df4a4190da4f4432d443521674637677dc91e61f", size = 9336221, upload-time = "2025-09-09T08:20:19.328Z" },
+ { url = "https://files.pythonhosted.org/packages/1c/ce/af9d99533b24c55ff4e18d9b7b4d9919bbc6cd8f22fe7a7be01519a347d5/scikit_learn-1.7.2-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:36749fb62b3d961b1ce4fedf08fa57a1986cd409eff2d783bca5d4b9b5fce51c", size = 8653834, upload-time = "2025-09-09T08:20:22.073Z" },
+ { url = "https://files.pythonhosted.org/packages/58/0e/8c2a03d518fb6bd0b6b0d4b114c63d5f1db01ff0f9925d8eb10960d01c01/scikit_learn-1.7.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7a58814265dfc52b3295b1900cfb5701589d30a8bb026c7540f1e9d3499d5ec8", size = 9660938, upload-time = "2025-09-09T08:20:24.327Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/75/4311605069b5d220e7cf5adabb38535bd96f0079313cdbb04b291479b22a/scikit_learn-1.7.2-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a847fea807e278f821a0406ca01e387f97653e284ecbd9750e3ee7c90347f18", size = 9477818, upload-time = "2025-09-09T08:20:26.845Z" },
+ { url = "https://files.pythonhosted.org/packages/7f/9b/87961813c34adbca21a6b3f6b2bea344c43b30217a6d24cc437c6147f3e8/scikit_learn-1.7.2-cp310-cp310-win_amd64.whl", hash = "sha256:ca250e6836d10e6f402436d6463d6c0e4d8e0234cfb6a9a47835bd392b852ce5", size = 8886969, upload-time = "2025-09-09T08:20:29.329Z" },
+ { url = "https://files.pythonhosted.org/packages/43/83/564e141eef908a5863a54da8ca342a137f45a0bfb71d1d79704c9894c9d1/scikit_learn-1.7.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c7509693451651cd7361d30ce4e86a1347493554f172b1c72a39300fa2aea79e", size = 9331967, upload-time = "2025-09-09T08:20:32.421Z" },
+ { url = "https://files.pythonhosted.org/packages/18/d6/ba863a4171ac9d7314c4d3fc251f015704a2caeee41ced89f321c049ed83/scikit_learn-1.7.2-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:0486c8f827c2e7b64837c731c8feff72c0bd2b998067a8a9cbc10643c31f0fe1", size = 8648645, upload-time = "2025-09-09T08:20:34.436Z" },
+ { url = "https://files.pythonhosted.org/packages/ef/0e/97dbca66347b8cf0ea8b529e6bb9367e337ba2e8be0ef5c1a545232abfde/scikit_learn-1.7.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:89877e19a80c7b11a2891a27c21c4894fb18e2c2e077815bcade10d34287b20d", size = 9715424, upload-time = "2025-09-09T08:20:36.776Z" },
+ { url = "https://files.pythonhosted.org/packages/f7/32/1f3b22e3207e1d2c883a7e09abb956362e7d1bd2f14458c7de258a26ac15/scikit_learn-1.7.2-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8da8bf89d4d79aaec192d2bda62f9b56ae4e5b4ef93b6a56b5de4977e375c1f1", size = 9509234, upload-time = "2025-09-09T08:20:38.957Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/71/34ddbd21f1da67c7a768146968b4d0220ee6831e4bcbad3e03dd3eae88b6/scikit_learn-1.7.2-cp311-cp311-win_amd64.whl", hash = "sha256:9b7ed8d58725030568523e937c43e56bc01cadb478fc43c042a9aca1dacb3ba1", size = 8894244, upload-time = "2025-09-09T08:20:41.166Z" },
+ { url = "https://files.pythonhosted.org/packages/a7/aa/3996e2196075689afb9fce0410ebdb4a09099d7964d061d7213700204409/scikit_learn-1.7.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:8d91a97fa2b706943822398ab943cde71858a50245e31bc71dba62aab1d60a96", size = 9259818, upload-time = "2025-09-09T08:20:43.19Z" },
+ { url = "https://files.pythonhosted.org/packages/43/5d/779320063e88af9c4a7c2cf463ff11c21ac9c8bd730c4a294b0000b666c9/scikit_learn-1.7.2-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:acbc0f5fd2edd3432a22c69bed78e837c70cf896cd7993d71d51ba6708507476", size = 8636997, upload-time = "2025-09-09T08:20:45.468Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/d0/0c577d9325b05594fdd33aa970bf53fb673f051a45496842caee13cfd7fe/scikit_learn-1.7.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e5bf3d930aee75a65478df91ac1225ff89cd28e9ac7bd1196853a9229b6adb0b", size = 9478381, upload-time = "2025-09-09T08:20:47.982Z" },
+ { url = "https://files.pythonhosted.org/packages/82/70/8bf44b933837ba8494ca0fc9a9ab60f1c13b062ad0197f60a56e2fc4c43e/scikit_learn-1.7.2-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b4d6e9deed1a47aca9fe2f267ab8e8fe82ee20b4526b2c0cd9e135cea10feb44", size = 9300296, upload-time = "2025-09-09T08:20:50.366Z" },
+ { url = "https://files.pythonhosted.org/packages/c6/99/ed35197a158f1fdc2fe7c3680e9c70d0128f662e1fee4ed495f4b5e13db0/scikit_learn-1.7.2-cp312-cp312-win_amd64.whl", hash = "sha256:6088aa475f0785e01bcf8529f55280a3d7d298679f50c0bb70a2364a82d0b290", size = 8731256, upload-time = "2025-09-09T08:20:52.627Z" },
+]
+
+[[package]]
+name = "scikit-learn"
+version = "1.8.0"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version >= '3.11'",
+]
+dependencies = [
+ { name = "joblib", marker = "python_full_version >= '3.11'" },
+ { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
+ { name = "scipy", version = "1.16.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
+ { name = "threadpoolctl", marker = "python_full_version >= '3.11'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/0e/d4/40988bf3b8e34feec1d0e6a051446b1f66225f8529b9309becaeef62b6c4/scikit_learn-1.8.0.tar.gz", hash = "sha256:9bccbb3b40e3de10351f8f5068e105d0f4083b1a65fa07b6634fbc401a6287fd", size = 7335585, upload-time = "2025-12-10T07:08:53.618Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c9/92/53ea2181da8ac6bf27170191028aee7251f8f841f8d3edbfdcaf2008fde9/scikit_learn-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:146b4d36f800c013d267b29168813f7a03a43ecd2895d04861f1240b564421da", size = 8595835, upload-time = "2025-12-10T07:07:39.385Z" },
+ { url = "https://files.pythonhosted.org/packages/01/18/d154dc1638803adf987910cdd07097d9c526663a55666a97c124d09fb96a/scikit_learn-1.8.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:f984ca4b14914e6b4094c5d52a32ea16b49832c03bd17a110f004db3c223e8e1", size = 8080381, upload-time = "2025-12-10T07:07:41.93Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/44/226142fcb7b7101e64fdee5f49dbe6288d4c7af8abf593237b70fca080a4/scikit_learn-1.8.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5e30adb87f0cc81c7690a84f7932dd66be5bac57cfe16b91cb9151683a4a2d3b", size = 8799632, upload-time = "2025-12-10T07:07:43.899Z" },
+ { url = "https://files.pythonhosted.org/packages/36/4d/4a67f30778a45d542bbea5db2dbfa1e9e100bf9ba64aefe34215ba9f11f6/scikit_learn-1.8.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ada8121bcb4dac28d930febc791a69f7cb1673c8495e5eee274190b73a4559c1", size = 9103788, upload-time = "2025-12-10T07:07:45.982Z" },
+ { url = "https://files.pythonhosted.org/packages/89/3c/45c352094cfa60050bcbb967b1faf246b22e93cb459f2f907b600f2ceda5/scikit_learn-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:c57b1b610bd1f40ba43970e11ce62821c2e6569e4d74023db19c6b26f246cb3b", size = 8081706, upload-time = "2025-12-10T07:07:48.111Z" },
+ { url = "https://files.pythonhosted.org/packages/3d/46/5416595bb395757f754feb20c3d776553a386b661658fb21b7c814e89efe/scikit_learn-1.8.0-cp311-cp311-win_arm64.whl", hash = "sha256:2838551e011a64e3053ad7618dda9310175f7515f1742fa2d756f7c874c05961", size = 7688451, upload-time = "2025-12-10T07:07:49.873Z" },
+ { url = "https://files.pythonhosted.org/packages/90/74/e6a7cc4b820e95cc38cf36cd74d5aa2b42e8ffc2d21fe5a9a9c45c1c7630/scikit_learn-1.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5fb63362b5a7ddab88e52b6dbb47dac3fd7dafeee740dc6c8d8a446ddedade8e", size = 8548242, upload-time = "2025-12-10T07:07:51.568Z" },
+ { url = "https://files.pythonhosted.org/packages/49/d8/9be608c6024d021041c7f0b3928d4749a706f4e2c3832bbede4fb4f58c95/scikit_learn-1.8.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:5025ce924beccb28298246e589c691fe1b8c1c96507e6d27d12c5fadd85bfd76", size = 8079075, upload-time = "2025-12-10T07:07:53.697Z" },
+ { url = "https://files.pythonhosted.org/packages/dd/47/f187b4636ff80cc63f21cd40b7b2d177134acaa10f6bb73746130ee8c2e5/scikit_learn-1.8.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4496bb2cf7a43ce1a2d7524a79e40bc5da45cf598dbf9545b7e8316ccba47bb4", size = 8660492, upload-time = "2025-12-10T07:07:55.574Z" },
+ { url = "https://files.pythonhosted.org/packages/97/74/b7a304feb2b49df9fafa9382d4d09061a96ee9a9449a7cbea7988dda0828/scikit_learn-1.8.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a0bcfe4d0d14aec44921545fd2af2338c7471de9cb701f1da4c9d85906ab847a", size = 8931904, upload-time = "2025-12-10T07:07:57.666Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/c4/0ab22726a04ede56f689476b760f98f8f46607caecff993017ac1b64aa5d/scikit_learn-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:35c007dedb2ffe38fe3ee7d201ebac4a2deccd2408e8621d53067733e3c74809", size = 8019359, upload-time = "2025-12-10T07:07:59.838Z" },
+ { url = "https://files.pythonhosted.org/packages/24/90/344a67811cfd561d7335c1b96ca21455e7e472d281c3c279c4d3f2300236/scikit_learn-1.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:8c497fff237d7b4e07e9ef1a640887fa4fb765647f86fbe00f969ff6280ce2bb", size = 7641898, upload-time = "2025-12-10T07:08:01.36Z" },
+]
+
+[[package]]
+name = "scipy"
+version = "1.15.3"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version < '3.11'",
+]
+dependencies = [
+ { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/0f/37/6964b830433e654ec7485e45a00fc9a27cf868d622838f6b6d9c5ec0d532/scipy-1.15.3.tar.gz", hash = "sha256:eae3cf522bc7df64b42cad3925c876e1b0b6c35c1337c93e12c0f366f55b0eaf", size = 59419214, upload-time = "2025-05-08T16:13:05.955Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/78/2f/4966032c5f8cc7e6a60f1b2e0ad686293b9474b65246b0c642e3ef3badd0/scipy-1.15.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:a345928c86d535060c9c2b25e71e87c39ab2f22fc96e9636bd74d1dbf9de448c", size = 38702770, upload-time = "2025-05-08T16:04:20.849Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/6e/0c3bf90fae0e910c274db43304ebe25a6b391327f3f10b5dcc638c090795/scipy-1.15.3-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:ad3432cb0f9ed87477a8d97f03b763fd1d57709f1bbde3c9369b1dff5503b253", size = 30094511, upload-time = "2025-05-08T16:04:27.103Z" },
+ { url = "https://files.pythonhosted.org/packages/ea/b1/4deb37252311c1acff7f101f6453f0440794f51b6eacb1aad4459a134081/scipy-1.15.3-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:aef683a9ae6eb00728a542b796f52a5477b78252edede72b8327a886ab63293f", size = 22368151, upload-time = "2025-05-08T16:04:31.731Z" },
+ { url = "https://files.pythonhosted.org/packages/38/7d/f457626e3cd3c29b3a49ca115a304cebb8cc6f31b04678f03b216899d3c6/scipy-1.15.3-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:1c832e1bd78dea67d5c16f786681b28dd695a8cb1fb90af2e27580d3d0967e92", size = 25121732, upload-time = "2025-05-08T16:04:36.596Z" },
+ { url = "https://files.pythonhosted.org/packages/db/0a/92b1de4a7adc7a15dcf5bddc6e191f6f29ee663b30511ce20467ef9b82e4/scipy-1.15.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:263961f658ce2165bbd7b99fa5135195c3a12d9bef045345016b8b50c315cb82", size = 35547617, upload-time = "2025-05-08T16:04:43.546Z" },
+ { url = "https://files.pythonhosted.org/packages/8e/6d/41991e503e51fc1134502694c5fa7a1671501a17ffa12716a4a9151af3df/scipy-1.15.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e2abc762b0811e09a0d3258abee2d98e0c703eee49464ce0069590846f31d40", size = 37662964, upload-time = "2025-05-08T16:04:49.431Z" },
+ { url = "https://files.pythonhosted.org/packages/25/e1/3df8f83cb15f3500478c889be8fb18700813b95e9e087328230b98d547ff/scipy-1.15.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ed7284b21a7a0c8f1b6e5977ac05396c0d008b89e05498c8b7e8f4a1423bba0e", size = 37238749, upload-time = "2025-05-08T16:04:55.215Z" },
+ { url = "https://files.pythonhosted.org/packages/93/3e/b3257cf446f2a3533ed7809757039016b74cd6f38271de91682aa844cfc5/scipy-1.15.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5380741e53df2c566f4d234b100a484b420af85deb39ea35a1cc1be84ff53a5c", size = 40022383, upload-time = "2025-05-08T16:05:01.914Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/84/55bc4881973d3f79b479a5a2e2df61c8c9a04fcb986a213ac9c02cfb659b/scipy-1.15.3-cp310-cp310-win_amd64.whl", hash = "sha256:9d61e97b186a57350f6d6fd72640f9e99d5a4a2b8fbf4b9ee9a841eab327dc13", size = 41259201, upload-time = "2025-05-08T16:05:08.166Z" },
+ { url = "https://files.pythonhosted.org/packages/96/ab/5cc9f80f28f6a7dff646c5756e559823614a42b1939d86dd0ed550470210/scipy-1.15.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:993439ce220d25e3696d1b23b233dd010169b62f6456488567e830654ee37a6b", size = 38714255, upload-time = "2025-05-08T16:05:14.596Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/4a/66ba30abe5ad1a3ad15bfb0b59d22174012e8056ff448cb1644deccbfed2/scipy-1.15.3-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:34716e281f181a02341ddeaad584205bd2fd3c242063bd3423d61ac259ca7eba", size = 30111035, upload-time = "2025-05-08T16:05:20.152Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/fa/a7e5b95afd80d24313307f03624acc65801846fa75599034f8ceb9e2cbf6/scipy-1.15.3-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3b0334816afb8b91dab859281b1b9786934392aa3d527cd847e41bb6f45bee65", size = 22384499, upload-time = "2025-05-08T16:05:24.494Z" },
+ { url = "https://files.pythonhosted.org/packages/17/99/f3aaddccf3588bb4aea70ba35328c204cadd89517a1612ecfda5b2dd9d7a/scipy-1.15.3-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:6db907c7368e3092e24919b5e31c76998b0ce1684d51a90943cb0ed1b4ffd6c1", size = 25152602, upload-time = "2025-05-08T16:05:29.313Z" },
+ { url = "https://files.pythonhosted.org/packages/56/c5/1032cdb565f146109212153339f9cb8b993701e9fe56b1c97699eee12586/scipy-1.15.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:721d6b4ef5dc82ca8968c25b111e307083d7ca9091bc38163fb89243e85e3889", size = 35503415, upload-time = "2025-05-08T16:05:34.699Z" },
+ { url = "https://files.pythonhosted.org/packages/bd/37/89f19c8c05505d0601ed5650156e50eb881ae3918786c8fd7262b4ee66d3/scipy-1.15.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39cb9c62e471b1bb3750066ecc3a3f3052b37751c7c3dfd0fd7e48900ed52982", size = 37652622, upload-time = "2025-05-08T16:05:40.762Z" },
+ { url = "https://files.pythonhosted.org/packages/7e/31/be59513aa9695519b18e1851bb9e487de66f2d31f835201f1b42f5d4d475/scipy-1.15.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:795c46999bae845966368a3c013e0e00947932d68e235702b5c3f6ea799aa8c9", size = 37244796, upload-time = "2025-05-08T16:05:48.119Z" },
+ { url = "https://files.pythonhosted.org/packages/10/c0/4f5f3eeccc235632aab79b27a74a9130c6c35df358129f7ac8b29f562ac7/scipy-1.15.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:18aaacb735ab38b38db42cb01f6b92a2d0d4b6aabefeb07f02849e47f8fb3594", size = 40047684, upload-time = "2025-05-08T16:05:54.22Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/a7/0ddaf514ce8a8714f6ed243a2b391b41dbb65251affe21ee3077ec45ea9a/scipy-1.15.3-cp311-cp311-win_amd64.whl", hash = "sha256:ae48a786a28412d744c62fd7816a4118ef97e5be0bee968ce8f0a2fba7acf3bb", size = 41246504, upload-time = "2025-05-08T16:06:00.437Z" },
+ { url = "https://files.pythonhosted.org/packages/37/4b/683aa044c4162e10ed7a7ea30527f2cbd92e6999c10a8ed8edb253836e9c/scipy-1.15.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6ac6310fdbfb7aa6612408bd2f07295bcbd3fda00d2d702178434751fe48e019", size = 38766735, upload-time = "2025-05-08T16:06:06.471Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/7e/f30be3d03de07f25dc0ec926d1681fed5c732d759ac8f51079708c79e680/scipy-1.15.3-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:185cd3d6d05ca4b44a8f1595af87f9c372bb6acf9c808e99aa3e9aa03bd98cf6", size = 30173284, upload-time = "2025-05-08T16:06:11.686Z" },
+ { url = "https://files.pythonhosted.org/packages/07/9c/0ddb0d0abdabe0d181c1793db51f02cd59e4901da6f9f7848e1f96759f0d/scipy-1.15.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:05dc6abcd105e1a29f95eada46d4a3f251743cfd7d3ae8ddb4088047f24ea477", size = 22446958, upload-time = "2025-05-08T16:06:15.97Z" },
+ { url = "https://files.pythonhosted.org/packages/af/43/0bce905a965f36c58ff80d8bea33f1f9351b05fad4beaad4eae34699b7a1/scipy-1.15.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:06efcba926324df1696931a57a176c80848ccd67ce6ad020c810736bfd58eb1c", size = 25242454, upload-time = "2025-05-08T16:06:20.394Z" },
+ { url = "https://files.pythonhosted.org/packages/56/30/a6f08f84ee5b7b28b4c597aca4cbe545535c39fe911845a96414700b64ba/scipy-1.15.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c05045d8b9bfd807ee1b9f38761993297b10b245f012b11b13b91ba8945f7e45", size = 35210199, upload-time = "2025-05-08T16:06:26.159Z" },
+ { url = "https://files.pythonhosted.org/packages/0b/1f/03f52c282437a168ee2c7c14a1a0d0781a9a4a8962d84ac05c06b4c5b555/scipy-1.15.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:271e3713e645149ea5ea3e97b57fdab61ce61333f97cfae392c28ba786f9bb49", size = 37309455, upload-time = "2025-05-08T16:06:32.778Z" },
+ { url = "https://files.pythonhosted.org/packages/89/b1/fbb53137f42c4bf630b1ffdfc2151a62d1d1b903b249f030d2b1c0280af8/scipy-1.15.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6cfd56fc1a8e53f6e89ba3a7a7251f7396412d655bca2aa5611c8ec9a6784a1e", size = 36885140, upload-time = "2025-05-08T16:06:39.249Z" },
+ { url = "https://files.pythonhosted.org/packages/2e/2e/025e39e339f5090df1ff266d021892694dbb7e63568edcfe43f892fa381d/scipy-1.15.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0ff17c0bb1cb32952c09217d8d1eed9b53d1463e5f1dd6052c7857f83127d539", size = 39710549, upload-time = "2025-05-08T16:06:45.729Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/eb/3bf6ea8ab7f1503dca3a10df2e4b9c3f6b3316df07f6c0ded94b281c7101/scipy-1.15.3-cp312-cp312-win_amd64.whl", hash = "sha256:52092bc0472cfd17df49ff17e70624345efece4e1a12b23783a1ac59a1b728ed", size = 40966184, upload-time = "2025-05-08T16:06:52.623Z" },
+]
+
+[[package]]
+name = "scipy"
+version = "1.16.3"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version >= '3.11'",
+]
+dependencies = [
+ { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/0a/ca/d8ace4f98322d01abcd52d381134344bf7b431eba7ed8b42bdea5a3c2ac9/scipy-1.16.3.tar.gz", hash = "sha256:01e87659402762f43bd2fee13370553a17ada367d42e7487800bf2916535aecb", size = 30597883, upload-time = "2025-10-28T17:38:54.068Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/9b/5f/6f37d7439de1455ce9c5a556b8d1db0979f03a796c030bafdf08d35b7bf9/scipy-1.16.3-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:40be6cf99e68b6c4321e9f8782e7d5ff8265af28ef2cd56e9c9b2638fa08ad97", size = 36630881, upload-time = "2025-10-28T17:31:47.104Z" },
+ { url = "https://files.pythonhosted.org/packages/7c/89/d70e9f628749b7e4db2aa4cd89735502ff3f08f7b9b27d2e799485987cd9/scipy-1.16.3-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:8be1ca9170fcb6223cc7c27f4305d680ded114a1567c0bd2bfcbf947d1b17511", size = 28941012, upload-time = "2025-10-28T17:31:53.411Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/a8/0e7a9a6872a923505dbdf6bb93451edcac120363131c19013044a1e7cb0c/scipy-1.16.3-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:bea0a62734d20d67608660f69dcda23e7f90fb4ca20974ab80b6ed40df87a005", size = 20931935, upload-time = "2025-10-28T17:31:57.361Z" },
+ { url = "https://files.pythonhosted.org/packages/bd/c7/020fb72bd79ad798e4dbe53938543ecb96b3a9ac3fe274b7189e23e27353/scipy-1.16.3-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:2a207a6ce9c24f1951241f4693ede2d393f59c07abc159b2cb2be980820e01fb", size = 23534466, upload-time = "2025-10-28T17:32:01.875Z" },
+ { url = "https://files.pythonhosted.org/packages/be/a0/668c4609ce6dbf2f948e167836ccaf897f95fb63fa231c87da7558a374cd/scipy-1.16.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:532fb5ad6a87e9e9cd9c959b106b73145a03f04c7d57ea3e6f6bb60b86ab0876", size = 33593618, upload-time = "2025-10-28T17:32:06.902Z" },
+ { url = "https://files.pythonhosted.org/packages/ca/6e/8942461cf2636cdae083e3eb72622a7fbbfa5cf559c7d13ab250a5dbdc01/scipy-1.16.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0151a0749efeaaab78711c78422d413c583b8cdd2011a3c1d6c794938ee9fdb2", size = 35899798, upload-time = "2025-10-28T17:32:12.665Z" },
+ { url = "https://files.pythonhosted.org/packages/79/e8/d0f33590364cdbd67f28ce79368b373889faa4ee959588beddf6daef9abe/scipy-1.16.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b7180967113560cca57418a7bc719e30366b47959dd845a93206fbed693c867e", size = 36226154, upload-time = "2025-10-28T17:32:17.961Z" },
+ { url = "https://files.pythonhosted.org/packages/39/c1/1903de608c0c924a1749c590064e65810f8046e437aba6be365abc4f7557/scipy-1.16.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:deb3841c925eeddb6afc1e4e4a45e418d19ec7b87c5df177695224078e8ec733", size = 38878540, upload-time = "2025-10-28T17:32:23.907Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/d0/22ec7036ba0b0a35bccb7f25ab407382ed34af0b111475eb301c16f8a2e5/scipy-1.16.3-cp311-cp311-win_amd64.whl", hash = "sha256:53c3844d527213631e886621df5695d35e4f6a75f620dca412bcd292f6b87d78", size = 38722107, upload-time = "2025-10-28T17:32:29.921Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/60/8a00e5a524bb3bf8898db1650d350f50e6cffb9d7a491c561dc9826c7515/scipy-1.16.3-cp311-cp311-win_arm64.whl", hash = "sha256:9452781bd879b14b6f055b26643703551320aa8d79ae064a71df55c00286a184", size = 25506272, upload-time = "2025-10-28T17:32:34.577Z" },
+ { url = "https://files.pythonhosted.org/packages/40/41/5bf55c3f386b1643812f3a5674edf74b26184378ef0f3e7c7a09a7e2ca7f/scipy-1.16.3-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:81fc5827606858cf71446a5e98715ba0e11f0dbc83d71c7409d05486592a45d6", size = 36659043, upload-time = "2025-10-28T17:32:40.285Z" },
+ { url = "https://files.pythonhosted.org/packages/1e/0f/65582071948cfc45d43e9870bf7ca5f0e0684e165d7c9ef4e50d783073eb/scipy-1.16.3-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:c97176013d404c7346bf57874eaac5187d969293bf40497140b0a2b2b7482e07", size = 28898986, upload-time = "2025-10-28T17:32:45.325Z" },
+ { url = "https://files.pythonhosted.org/packages/96/5e/36bf3f0ac298187d1ceadde9051177d6a4fe4d507e8f59067dc9dd39e650/scipy-1.16.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:2b71d93c8a9936046866acebc915e2af2e292b883ed6e2cbe5c34beb094b82d9", size = 20889814, upload-time = "2025-10-28T17:32:49.277Z" },
+ { url = "https://files.pythonhosted.org/packages/80/35/178d9d0c35394d5d5211bbff7ac4f2986c5488b59506fef9e1de13ea28d3/scipy-1.16.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:3d4a07a8e785d80289dfe66b7c27d8634a773020742ec7187b85ccc4b0e7b686", size = 23565795, upload-time = "2025-10-28T17:32:53.337Z" },
+ { url = "https://files.pythonhosted.org/packages/fa/46/d1146ff536d034d02f83c8afc3c4bab2eddb634624d6529a8512f3afc9da/scipy-1.16.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0553371015692a898e1aa858fed67a3576c34edefa6b7ebdb4e9dde49ce5c203", size = 33349476, upload-time = "2025-10-28T17:32:58.353Z" },
+ { url = "https://files.pythonhosted.org/packages/79/2e/415119c9ab3e62249e18c2b082c07aff907a273741b3f8160414b0e9193c/scipy-1.16.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:72d1717fd3b5e6ec747327ce9bda32d5463f472c9dce9f54499e81fbd50245a1", size = 35676692, upload-time = "2025-10-28T17:33:03.88Z" },
+ { url = "https://files.pythonhosted.org/packages/27/82/df26e44da78bf8d2aeaf7566082260cfa15955a5a6e96e6a29935b64132f/scipy-1.16.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1fb2472e72e24d1530debe6ae078db70fb1605350c88a3d14bc401d6306dbffe", size = 36019345, upload-time = "2025-10-28T17:33:09.773Z" },
+ { url = "https://files.pythonhosted.org/packages/82/31/006cbb4b648ba379a95c87262c2855cd0d09453e500937f78b30f02fa1cd/scipy-1.16.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c5192722cffe15f9329a3948c4b1db789fbb1f05c97899187dcf009b283aea70", size = 38678975, upload-time = "2025-10-28T17:33:15.809Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/7f/acbd28c97e990b421af7d6d6cd416358c9c293fc958b8529e0bd5d2a2a19/scipy-1.16.3-cp312-cp312-win_amd64.whl", hash = "sha256:56edc65510d1331dae01ef9b658d428e33ed48b4f77b1d51caf479a0253f96dc", size = 38555926, upload-time = "2025-10-28T17:33:21.388Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/69/c5c7807fd007dad4f48e0a5f2153038dc96e8725d3345b9ee31b2b7bed46/scipy-1.16.3-cp312-cp312-win_arm64.whl", hash = "sha256:a8a26c78ef223d3e30920ef759e25625a0ecdd0d60e5a8818b7513c3e5384cf2", size = 25463014, upload-time = "2025-10-28T17:33:25.975Z" },
+]
+
+[[package]]
+name = "threadpoolctl"
+version = "3.6.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/b7/4d/08c89e34946fce2aec4fbb45c9016efd5f4d7f24af8e5d93296e935631d8/threadpoolctl-3.6.0.tar.gz", hash = "sha256:8ab8b4aa3491d812b623328249fab5302a68d2d71745c8a4c719a2fcaba9f44e", size = 21274, upload-time = "2025-03-13T13:49:23.031Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/32/d5/f9a850d79b0851d1d4ef6456097579a9005b31fea68726a4ae5f2d82ddd9/threadpoolctl-3.6.0-py3-none-any.whl", hash = "sha256:43a0b8fd5a2928500110039e43a5eed8480b918967083ea48dc3ab9f13c4a7fb", size = 18638, upload-time = "2025-03-13T13:49:21.846Z" },
+]
+
+[[package]]
+name = "tomli"
+version = "2.3.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/52/ed/3f73f72945444548f33eba9a87fc7a6e969915e7b1acc8260b30e1f76a2f/tomli-2.3.0.tar.gz", hash = "sha256:64be704a875d2a59753d80ee8a533c3fe183e3f06807ff7dc2232938ccb01549", size = 17392, upload-time = "2025-10-08T22:01:47.119Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b3/2e/299f62b401438d5fe1624119c723f5d877acc86a4c2492da405626665f12/tomli-2.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:88bd15eb972f3664f5ed4b57c1634a97153b4bac4479dcb6a495f41921eb7f45", size = 153236, upload-time = "2025-10-08T22:01:00.137Z" },
+ { url = "https://files.pythonhosted.org/packages/86/7f/d8fffe6a7aefdb61bced88fcb5e280cfd71e08939da5894161bd71bea022/tomli-2.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:883b1c0d6398a6a9d29b508c331fa56adbcdff647f6ace4dfca0f50e90dfd0ba", size = 148084, upload-time = "2025-10-08T22:01:01.63Z" },
+ { url = "https://files.pythonhosted.org/packages/47/5c/24935fb6a2ee63e86d80e4d3b58b222dafaf438c416752c8b58537c8b89a/tomli-2.3.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d1381caf13ab9f300e30dd8feadb3de072aeb86f1d34a8569453ff32a7dea4bf", size = 234832, upload-time = "2025-10-08T22:01:02.543Z" },
+ { url = "https://files.pythonhosted.org/packages/89/da/75dfd804fc11e6612846758a23f13271b76d577e299592b4371a4ca4cd09/tomli-2.3.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a0e285d2649b78c0d9027570d4da3425bdb49830a6156121360b3f8511ea3441", size = 242052, upload-time = "2025-10-08T22:01:03.836Z" },
+ { url = "https://files.pythonhosted.org/packages/70/8c/f48ac899f7b3ca7eb13af73bacbc93aec37f9c954df3c08ad96991c8c373/tomli-2.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0a154a9ae14bfcf5d8917a59b51ffd5a3ac1fd149b71b47a3a104ca4edcfa845", size = 239555, upload-time = "2025-10-08T22:01:04.834Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/28/72f8afd73f1d0e7829bfc093f4cb98ce0a40ffc0cc997009ee1ed94ba705/tomli-2.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:74bf8464ff93e413514fefd2be591c3b0b23231a77f901db1eb30d6f712fc42c", size = 245128, upload-time = "2025-10-08T22:01:05.84Z" },
+ { url = "https://files.pythonhosted.org/packages/b6/eb/a7679c8ac85208706d27436e8d421dfa39d4c914dcf5fa8083a9305f58d9/tomli-2.3.0-cp311-cp311-win32.whl", hash = "sha256:00b5f5d95bbfc7d12f91ad8c593a1659b6387b43f054104cda404be6bda62456", size = 96445, upload-time = "2025-10-08T22:01:06.896Z" },
+ { url = "https://files.pythonhosted.org/packages/0a/fe/3d3420c4cb1ad9cb462fb52967080575f15898da97e21cb6f1361d505383/tomli-2.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:4dc4ce8483a5d429ab602f111a93a6ab1ed425eae3122032db7e9acf449451be", size = 107165, upload-time = "2025-10-08T22:01:08.107Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/b7/40f36368fcabc518bb11c8f06379a0fd631985046c038aca08c6d6a43c6e/tomli-2.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d7d86942e56ded512a594786a5ba0a5e521d02529b3826e7761a05138341a2ac", size = 154891, upload-time = "2025-10-08T22:01:09.082Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/3f/d9dd692199e3b3aab2e4e4dd948abd0f790d9ded8cd10cbaae276a898434/tomli-2.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:73ee0b47d4dad1c5e996e3cd33b8a76a50167ae5f96a2607cbe8cc773506ab22", size = 148796, upload-time = "2025-10-08T22:01:10.266Z" },
+ { url = "https://files.pythonhosted.org/packages/60/83/59bff4996c2cf9f9387a0f5a3394629c7efa5ef16142076a23a90f1955fa/tomli-2.3.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:792262b94d5d0a466afb5bc63c7daa9d75520110971ee269152083270998316f", size = 242121, upload-time = "2025-10-08T22:01:11.332Z" },
+ { url = "https://files.pythonhosted.org/packages/45/e5/7c5119ff39de8693d6baab6c0b6dcb556d192c165596e9fc231ea1052041/tomli-2.3.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4f195fe57ecceac95a66a75ac24d9d5fbc98ef0962e09b2eddec5d39375aae52", size = 250070, upload-time = "2025-10-08T22:01:12.498Z" },
+ { url = "https://files.pythonhosted.org/packages/45/12/ad5126d3a278f27e6701abde51d342aa78d06e27ce2bb596a01f7709a5a2/tomli-2.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e31d432427dcbf4d86958c184b9bfd1e96b5b71f8eb17e6d02531f434fd335b8", size = 245859, upload-time = "2025-10-08T22:01:13.551Z" },
+ { url = "https://files.pythonhosted.org/packages/fb/a1/4d6865da6a71c603cfe6ad0e6556c73c76548557a8d658f9e3b142df245f/tomli-2.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7b0882799624980785240ab732537fcfc372601015c00f7fc367c55308c186f6", size = 250296, upload-time = "2025-10-08T22:01:14.614Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/b7/a7a7042715d55c9ba6e8b196d65d2cb662578b4d8cd17d882d45322b0d78/tomli-2.3.0-cp312-cp312-win32.whl", hash = "sha256:ff72b71b5d10d22ecb084d345fc26f42b5143c5533db5e2eaba7d2d335358876", size = 97124, upload-time = "2025-10-08T22:01:15.629Z" },
+ { url = "https://files.pythonhosted.org/packages/06/1e/f22f100db15a68b520664eb3328fb0ae4e90530887928558112c8d1f4515/tomli-2.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:1cb4ed918939151a03f33d4242ccd0aa5f11b3547d0cf30f7c74a408a5b99878", size = 107698, upload-time = "2025-10-08T22:01:16.51Z" },
+ { url = "https://files.pythonhosted.org/packages/77/b8/0135fadc89e73be292b473cb820b4f5a08197779206b33191e801feeae40/tomli-2.3.0-py3-none-any.whl", hash = "sha256:e95b1af3c5b07d9e643909b5abbec77cd9f1217e6d0bca72b0234736b9fb1f1b", size = 14408, upload-time = "2025-10-08T22:01:46.04Z" },
+]
+
+[[package]]
+name = "typing-extensions"
+version = "4.15.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" },
+]
+
+[[package]]
+name = "useful-optimizer"
+version = "0.1.2"
+source = { editable = "." }
+dependencies = [
+ { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
+ { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
+ { name = "scikit-learn", version = "1.7.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
+ { name = "scikit-learn", version = "1.8.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
+ { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
+ { name = "scipy", version = "1.16.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
+]
+
+[package.optional-dependencies]
+dev = [
+ { name = "pre-commit" },
+ { name = "pytest" },
+ { name = "ruff" },
+]
+
+[package.dev-dependencies]
+dev = [
+ { name = "pre-commit" },
+ { name = "pytest" },
+ { name = "ruff" },
+]
+
+[package.metadata]
+requires-dist = [
+ { name = "numpy", specifier = ">=1.26.4" },
+ { name = "pre-commit", marker = "extra == 'dev'", specifier = ">=4.0.0" },
+ { name = "pytest", marker = "extra == 'dev'", specifier = ">=8.0.0" },
+ { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.9.0" },
+ { name = "scikit-learn", specifier = ">=1.5.1" },
+ { name = "scipy", specifier = ">=1.12.0" },
+]
+provides-extras = ["dev"]
+
+[package.metadata.requires-dev]
+dev = [
+ { name = "pre-commit", specifier = ">=4.0.0" },
+ { name = "pytest", specifier = ">=8.0.0" },
+ { name = "ruff", specifier = ">=0.9.0" },
+]
+
+[[package]]
+name = "virtualenv"
+version = "20.35.4"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "distlib" },
+ { name = "filelock" },
+ { name = "platformdirs" },
+ { name = "typing-extensions", marker = "python_full_version < '3.11'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/20/28/e6f1a6f655d620846bd9df527390ecc26b3805a0c5989048c210e22c5ca9/virtualenv-20.35.4.tar.gz", hash = "sha256:643d3914d73d3eeb0c552cbb12d7e82adf0e504dbf86a3182f8771a153a1971c", size = 6028799, upload-time = "2025-10-29T06:57:40.511Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/79/0c/c05523fa3181fdf0c9c52a6ba91a23fbf3246cc095f26f6516f9c60e6771/virtualenv-20.35.4-py3-none-any.whl", hash = "sha256:c21c9cede36c9753eeade68ba7d523529f228a403463376cf821eaae2b650f1b", size = 6005095, upload-time = "2025-10-29T06:57:37.598Z" },
+]