diff --git a/opt/CONSTANTS.md b/opt/CONSTANTS.md new file mode 100644 index 00000000..47adac1b --- /dev/null +++ b/opt/CONSTANTS.md @@ -0,0 +1,180 @@ +# Constants Module + +This module centralizes magic numbers and default values used throughout the useful-optimizer library, improving code maintainability and reducing code quality violations. + +## Purpose + +The `opt/constants.py` module addresses the following goals: + +1. **Eliminate Magic Numbers**: Replace hard-coded values with named constants for better readability +2. **Centralize Configuration**: Provide a single source of truth for algorithm parameters +3. **Improve Code Quality**: Reduce linting violations (PLR2004 - Magic value comparisons) +4. **Enhance Documentation**: Each constant includes a docstring explaining its purpose + +## Organization + +Constants are organized into the following categories: + +### Population and Iteration Defaults +- `DEFAULT_POPULATION_SIZE` - Default population size for population-based algorithms +- `DEFAULT_MAX_ITERATIONS` - Default maximum number of iterations +- `DEFAULT_SEED` - Default random seed for reproducibility + +### Convergence and Tolerance Thresholds +- `DEFAULT_TOLERANCE` - General convergence tolerance +- `DEFAULT_CONVERGENCE_THRESHOLD` - Stricter convergence threshold +- `BARRIER_METHOD_MIN_MU` - Minimum barrier parameter +- `PENALTY_METHOD_TOLERANCE` - Penalty method tolerance + +### Numerical Stability Constants +- `EPSILON_STABILITY` - Small constant for numerical stability +- `EPSILON_GRADIENT` - Epsilon for gradient computation +- `ADAM_EPSILON` - Epsilon for Adam-family optimizers + +### Particle Swarm Optimization (PSO) Constants +- `PSO_INERTIA_WEIGHT` - Inertia weight for velocity updates +- `PSO_COGNITIVE_COEFFICIENT` - Cognitive coefficient (c1) +- `PSO_SOCIAL_COEFFICIENT` - Social coefficient (c2) +- `PSO_CONSTRICTION_COEFFICIENT` - Constriction coefficient +- `PSO_ACCELERATION_COEFFICIENT` - Acceleration coefficient + +### Gradient-Based Optimizer Constants +- `ADAM_BETA1` - First moment decay for Adam-family optimizers +- `ADAM_BETA2` - Second moment decay for Adam-family optimizers +- `ADAMW_LEARNING_RATE` - Default learning rate for AdamW +- `NADAM_LEARNING_RATE` - Default learning rate for Nadam +- `SGD_MOMENTUM` - Momentum coefficient for SGD +- `RMSPROP_DECAY_RATE` - Decay rate for RMSprop + +### Algorithm-Specific Constants +- `GOLDEN_RATIO` - The golden ratio (φ ≈ 1.618) +- `ELITE_FRACTION` - Elite sample fraction in cross-entropy method +- `TRAINING_PROBABILITY` - Training phase probability +- `BANDWIDTH_DEFAULT` - Default KDE bandwidth +- `GAMMA_DEFAULT` - Default gamma parameter + +### Benchmark Function Constants +- `ACKLEY_A`, `ACKLEY_B`, `ACKLEY_C` - Ackley function constants +- `MATYAS_COEFFICIENT_A`, `MATYAS_COEFFICIENT_B` - Matyas function coefficients + +### Bound Defaults +- `SHIFTED_ACKLEY_BOUND` - Typical bound for shifted Ackley function +- `ACKLEY_BOUND` - Typical bound for standard Ackley function +- `SPHERE_BOUND` - Typical bound for sphere function +- `ROSENBROCK_BOUND` - Typical bound for Rosenbrock function + +## Usage + +### In Optimizer Implementations + +```python +from opt.constants import ( + DEFAULT_MAX_ITERATIONS, + DEFAULT_POPULATION_SIZE, + PSO_COGNITIVE_COEFFICIENT, + PSO_INERTIA_WEIGHT, + PSO_SOCIAL_COEFFICIENT, +) + +class ParticleSwarm(AbstractOptimizer): + def __init__( + self, + func: Callable, + lower_bound: float, + upper_bound: float, + dim: int, + population_size: int = DEFAULT_POPULATION_SIZE, + max_iter: int = DEFAULT_MAX_ITERATIONS, + c1: float = PSO_COGNITIVE_COEFFICIENT, + c2: float = PSO_SOCIAL_COEFFICIENT, + w: float = PSO_INERTIA_WEIGHT, + # ... other parameters + ) -> None: + # Implementation +``` + +### In Test Code + +```python +from opt.constants import ( + SHIFTED_ACKLEY_BOUND, + DEFAULT_MAX_ITERATIONS, +) + +def test_optimizer(): + optimizer = SomeOptimizer( + func=shifted_ackley, + lower_bound=-SHIFTED_ACKLEY_BOUND, + upper_bound=SHIFTED_ACKLEY_BOUND, + dim=2, + max_iter=DEFAULT_MAX_ITERATIONS, + ) +``` + +## Migration Status + +### Completed ✅ +- [x] `opt/abstract_optimizer.py` - Base optimizer class +- [x] `opt/swarm_intelligence/particle_swarm.py` - PSO algorithm +- [x] `opt/gradient_based/nadam.py` - Nadam optimizer +- [x] `opt/gradient_based/adamw.py` - AdamW optimizer +- [x] Test suite for constants module + +### In Progress 🚧 +- [ ] Remaining gradient-based optimizers (adadelta, adagrad, adamax, amsgrad, rmsprop, sgd_momentum) +- [ ] Swarm intelligence algorithms (ant_colony, bat_algorithm, firefly, etc.) +- [ ] Evolutionary algorithms +- [ ] Physics-inspired algorithms +- [ ] Social-inspired algorithms +- [ ] Multi-objective algorithms +- [ ] Constrained optimization algorithms + +### Migration Strategy + +The migration is being done incrementally to: +1. Minimize disruption to the codebase +2. Allow thorough testing of each change +3. Maintain backward compatibility +4. Gradually reduce PLR2004 linting violations + +## Benefits + +1. **Code Readability**: `PSO_INERTIA_WEIGHT` is more readable than `0.5` +2. **Maintainability**: Update a constant in one place rather than searching all files +3. **Consistency**: Ensures same default values across all algorithms +4. **Documentation**: Each constant has a docstring explaining its purpose +5. **Testing**: Constants can be unit tested for correctness +6. **Code Quality**: Reduces linting violations and technical debt + +## References + +Constants are based on: +- Published literature (citations in algorithm implementations) +- Common practice in optimization research +- Empirical testing and validation +- Numerical stability requirements + +## Contributing + +When adding new optimization algorithms or updating existing ones: + +1. Check if needed constants already exist in `opt/constants.py` +2. If not, add new constants with descriptive names and docstrings +3. Group related constants together +4. Update this README with the new constants +5. Add tests to `opt/test/test_constants.py` + +## Testing + +The constants module includes comprehensive tests: + +```bash +uv run pytest opt/test/test_constants.py -v +``` + +Tests verify: +- Constants have expected types +- Constants are in valid ranges +- Relationships between constants are maintained +- No unintended zero values +- Mathematical relationships (e.g., golden ratio) diff --git a/opt/abstract_optimizer.py b/opt/abstract_optimizer.py index c612ddfb..648adb29 100644 --- a/opt/abstract_optimizer.py +++ b/opt/abstract_optimizer.py @@ -8,6 +8,11 @@ import numpy as np +from opt.constants import DEFAULT_MAX_ITERATIONS +from opt.constants import DEFAULT_POPULATION_SIZE +from opt.constants import DEFAULT_SEED +from opt.constants import POWER_THIRTY_TWO + if TYPE_CHECKING: from collections.abc import Callable @@ -52,9 +57,9 @@ def __init__( lower_bound: float, upper_bound: float, dim: int, - max_iter: int = 1000, + max_iter: int = DEFAULT_MAX_ITERATIONS, seed: int | None = None, - population_size: int = 100, + population_size: int = DEFAULT_POPULATION_SIZE, track_history: bool = False, ) -> None: """Initialize the optimizer.""" @@ -64,7 +69,9 @@ def __init__( self.dim = dim self.max_iter = max_iter if seed is None: - self.seed = np.random.default_rng(42).integers(0, 2**32) + self.seed = np.random.default_rng(DEFAULT_SEED).integers( + 0, 2**POWER_THIRTY_TWO + ) else: self.seed = seed self.population_size = population_size diff --git a/opt/constants.py b/opt/constants.py new file mode 100644 index 00000000..b9f79b2d --- /dev/null +++ b/opt/constants.py @@ -0,0 +1,194 @@ +"""Constants used across optimization algorithms. + +This module centralizes magic numbers and default values used throughout the +optimization library, making them explicit, documented, and maintainable. + +Constants are organized into categories: + - Population and iteration defaults + - Convergence and tolerance thresholds + - Numerical stability constants + - Algorithm-specific parameters (PSO, gradient-based, etc.) +""" + +from __future__ import annotations + +import numpy as np + + +# ============================================================================= +# Population and Iteration Defaults +# ============================================================================= + +DEFAULT_POPULATION_SIZE = 100 +"""Default number of individuals in population-based algorithms.""" + +DEFAULT_MAX_ITERATIONS = 1000 +"""Default maximum number of optimization iterations.""" + +DEFAULT_SEED = 42 +"""Default random seed for reproducibility.""" + +# ============================================================================= +# Convergence and Tolerance Thresholds +# ============================================================================= + +DEFAULT_TOLERANCE = 1e-6 +"""Default convergence tolerance for optimization algorithms.""" + +DEFAULT_CONVERGENCE_THRESHOLD = 1e-8 +"""Default threshold for determining convergence.""" + +BARRIER_METHOD_MIN_MU = 1e-10 +"""Minimum barrier parameter for barrier method termination.""" + +PENALTY_METHOD_TOLERANCE = 1e-6 +"""Tolerance for penalty method constraint satisfaction.""" + +# ============================================================================= +# Numerical Stability Constants +# ============================================================================= + +EPSILON_STABILITY = 1e-8 +"""Small constant for numerical stability in gradient-based methods.""" + +EPSILON_GRADIENT = 1e-10 +"""Epsilon for gradient computation numerical stability.""" + +# ============================================================================= +# Particle Swarm Optimization (PSO) Constants +# ============================================================================= + +PSO_INERTIA_WEIGHT = 0.5 +"""Default inertia weight for PSO velocity updates.""" + +PSO_COGNITIVE_COEFFICIENT = 1.5 +"""Default cognitive coefficient (c1) for PSO.""" + +PSO_SOCIAL_COEFFICIENT = 1.5 +"""Default social coefficient (c2) for PSO.""" + +PSO_CONSTRICTION_COEFFICIENT = 0.7298 +"""Constriction coefficient for PSO (Clerc and Kennedy, 2002).""" + +PSO_ACCELERATION_COEFFICIENT = 1.49618 +"""Acceleration coefficient for PSO (common in literature).""" + +# ============================================================================= +# Gradient-Based Optimizer Constants +# ============================================================================= + +ADAM_BETA1 = 0.9 +"""Default beta1 (first moment decay) for Adam-family optimizers.""" + +ADAM_BETA2 = 0.999 +"""Default beta2 (second moment decay) for Adam-family optimizers.""" + +ADAM_EPSILON = 1e-8 +"""Default epsilon for numerical stability in Adam-family optimizers.""" + +ADAMW_LEARNING_RATE = 0.001 +"""Default learning rate for AdamW optimizer.""" + +ADAMW_WEIGHT_DECAY = 0.01 +"""Default weight decay coefficient for AdamW optimizer.""" + +NADAM_LEARNING_RATE = 0.002 +"""Default learning rate for Nadam optimizer.""" + +SGD_MOMENTUM = 0.9 +"""Default momentum coefficient for SGD with momentum.""" + +RMSPROP_DECAY_RATE = 0.9 +"""Default decay rate for RMSprop optimizer.""" + +# ============================================================================= +# Algorithm-Specific Constants +# ============================================================================= + +GOLDEN_RATIO = 1.618033988749895 +"""The golden ratio, used in various optimization algorithms.""" + +ELITE_FRACTION = 0.2 +"""Default fraction of elite samples in cross-entropy method.""" + +TRAINING_PROBABILITY = 0.2 +"""Default probability for training phase in certain algorithms.""" + +SOCIAL_COEFFICIENT = 0.2 +"""Default self-introspection coefficient for social algorithms.""" + +BANDWIDTH_DEFAULT = 0.2 +"""Default bandwidth for kernel density estimation.""" + +GAMMA_DEFAULT = 0.15 +"""Default gamma parameter for various algorithms.""" + +BETA_ATTRACTION = 0.2 +"""Default beta multiplier for attraction/repulsion forces.""" + +ACKLEY_A = 20.0 +"""Ackley function constant 'a'.""" + +ACKLEY_B = 0.2 +"""Ackley function constant 'b'.""" + +ACKLEY_C = 2.0 * np.pi +"""Ackley function constant 'c' (typically 2*pi).""" + +MATYAS_COEFFICIENT_A = 0.26 +"""Matyas function coefficient for squared terms.""" + +MATYAS_COEFFICIENT_B = 0.48 +"""Matyas function coefficient for cross term.""" + +# ============================================================================= +# Multi-Objective Constants +# ============================================================================= + +WEIGHT_ADJUSTMENT_MIN = 1e-6 +"""Minimum weight value for numerical stability in multi-objective algorithms.""" + +# ============================================================================= +# Probability and Fraction Constants +# ============================================================================= + +PROBABILITY_HALF = 0.5 +"""Probability value of 0.5 for binary decisions.""" + +FRACTION_QUARTER = 0.25 +"""Fraction value of 0.25 (25%).""" + +FRACTION_THIRD = 0.33 +"""Fraction value of 0.33 (approximately 1/3).""" + +FRACTION_TWO_THIRDS = 0.67 +"""Fraction value of 0.67 (approximately 2/3).""" + +# ============================================================================= +# Dimension and Bound Defaults +# ============================================================================= + +DEFAULT_DIM = 2 +"""Default dimensionality for test problems.""" + +SHIFTED_ACKLEY_BOUND = 2.768 +"""Typical bound for shifted Ackley function.""" + +ACKLEY_BOUND = 32.768 +"""Typical bound for standard Ackley function.""" + +SPHERE_BOUND = 5.0 +"""Typical bound for sphere function.""" + +ROSENBROCK_BOUND = 5.0 +"""Typical bound for Rosenbrock function.""" + +# ============================================================================= +# Power and Exponent Constants +# ============================================================================= + +POWER_TWO = 2 +"""Exponent value of 2 for squaring operations.""" + +POWER_THIRTY_TWO = 32 +"""Exponent value of 32 for random seed generation (2^32).""" diff --git a/opt/gradient_based/adamw.py b/opt/gradient_based/adamw.py index 9622e31a..304e2abf 100644 --- a/opt/gradient_based/adamw.py +++ b/opt/gradient_based/adamw.py @@ -47,6 +47,12 @@ from scipy.optimize import approx_fprime from opt.abstract_optimizer import AbstractOptimizer +from opt.constants import ADAMW_LEARNING_RATE +from opt.constants import ADAMW_WEIGHT_DECAY +from opt.constants import ADAM_BETA1 +from opt.constants import ADAM_BETA2 +from opt.constants import ADAM_EPSILON +from opt.constants import DEFAULT_MAX_ITERATIONS if TYPE_CHECKING: @@ -78,12 +84,12 @@ def __init__( lower_bound: float, upper_bound: float, dim: int, - max_iter: int = 1000, - learning_rate: float = 0.001, - beta1: float = 0.9, - beta2: float = 0.999, - epsilon: float = 1e-8, - weight_decay: float = 0.01, + max_iter: int = DEFAULT_MAX_ITERATIONS, + learning_rate: float = ADAMW_LEARNING_RATE, + beta1: float = ADAM_BETA1, + beta2: float = ADAM_BETA2, + epsilon: float = ADAM_EPSILON, + weight_decay: float = ADAMW_WEIGHT_DECAY, seed: int | None = None, ) -> None: """Initialize the AdamW optimizer.""" diff --git a/opt/gradient_based/nadam.py b/opt/gradient_based/nadam.py index 156b5917..ee185c57 100644 --- a/opt/gradient_based/nadam.py +++ b/opt/gradient_based/nadam.py @@ -47,6 +47,11 @@ from scipy.optimize import approx_fprime from opt.abstract_optimizer import AbstractOptimizer +from opt.constants import ADAM_BETA1 +from opt.constants import ADAM_BETA2 +from opt.constants import ADAM_EPSILON +from opt.constants import DEFAULT_MAX_ITERATIONS +from opt.constants import NADAM_LEARNING_RATE if TYPE_CHECKING: @@ -77,11 +82,11 @@ def __init__( lower_bound: float, upper_bound: float, dim: int, - max_iter: int = 1000, - learning_rate: float = 0.002, - beta1: float = 0.9, - beta2: float = 0.999, - epsilon: float = 1e-8, + max_iter: int = DEFAULT_MAX_ITERATIONS, + learning_rate: float = NADAM_LEARNING_RATE, + beta1: float = ADAM_BETA1, + beta2: float = ADAM_BETA2, + epsilon: float = ADAM_EPSILON, seed: int | None = None, ) -> None: """Initialize the Nadam optimizer.""" diff --git a/opt/swarm_intelligence/particle_swarm.py b/opt/swarm_intelligence/particle_swarm.py index df76d782..b1684ab6 100644 --- a/opt/swarm_intelligence/particle_swarm.py +++ b/opt/swarm_intelligence/particle_swarm.py @@ -30,6 +30,11 @@ import numpy as np from opt.abstract_optimizer import AbstractOptimizer +from opt.constants import DEFAULT_MAX_ITERATIONS +from opt.constants import DEFAULT_POPULATION_SIZE +from opt.constants import PSO_COGNITIVE_COEFFICIENT +from opt.constants import PSO_INERTIA_WEIGHT +from opt.constants import PSO_SOCIAL_COEFFICIENT if TYPE_CHECKING: @@ -73,11 +78,11 @@ def __init__( lower_bound: float, upper_bound: float, dim: int, - population_size: int = 100, - max_iter: int = 1000, - c1: float = 1.5, - c2: float = 1.5, - w: float = 0.5, + population_size: int = DEFAULT_POPULATION_SIZE, + max_iter: int = DEFAULT_MAX_ITERATIONS, + c1: float = PSO_COGNITIVE_COEFFICIENT, + c2: float = PSO_SOCIAL_COEFFICIENT, + w: float = PSO_INERTIA_WEIGHT, seed: int | None = None, track_history: bool = False, ) -> None: diff --git a/opt/test/test_constants.py b/opt/test/test_constants.py new file mode 100644 index 00000000..e9097418 --- /dev/null +++ b/opt/test/test_constants.py @@ -0,0 +1,188 @@ +"""Tests for the constants module. + +This module verifies that constants are properly defined and maintain +expected relationships (e.g., tolerances are smaller than bounds). +""" + +from __future__ import annotations + +from opt.constants import ACKLEY_BOUND +from opt.constants import ADAMW_LEARNING_RATE +from opt.constants import ADAMW_WEIGHT_DECAY +from opt.constants import ADAM_BETA1 +from opt.constants import ADAM_BETA2 +from opt.constants import ADAM_EPSILON +from opt.constants import BARRIER_METHOD_MIN_MU +from opt.constants import DEFAULT_CONVERGENCE_THRESHOLD +from opt.constants import DEFAULT_MAX_ITERATIONS +from opt.constants import DEFAULT_POPULATION_SIZE +from opt.constants import DEFAULT_SEED +from opt.constants import DEFAULT_TOLERANCE +from opt.constants import ELITE_FRACTION +from opt.constants import EPSILON_STABILITY +from opt.constants import GOLDEN_RATIO +from opt.constants import NADAM_LEARNING_RATE +from opt.constants import PENALTY_METHOD_TOLERANCE +from opt.constants import POWER_THIRTY_TWO +from opt.constants import PSO_COGNITIVE_COEFFICIENT +from opt.constants import PSO_INERTIA_WEIGHT +from opt.constants import PSO_SOCIAL_COEFFICIENT +from opt.constants import ROSENBROCK_BOUND +from opt.constants import SHIFTED_ACKLEY_BOUND +from opt.constants import SPHERE_BOUND + + +class TestDefaultConstants: + """Test default parameter constants.""" + + def test_default_population_size(self) -> None: + """Test default population size is positive.""" + assert DEFAULT_POPULATION_SIZE > 0 + assert isinstance(DEFAULT_POPULATION_SIZE, int) + + def test_default_max_iterations(self) -> None: + """Test default max iterations is positive.""" + assert DEFAULT_MAX_ITERATIONS > 0 + assert isinstance(DEFAULT_MAX_ITERATIONS, int) + + def test_default_seed(self) -> None: + """Test default seed is non-negative.""" + assert DEFAULT_SEED >= 0 + assert isinstance(DEFAULT_SEED, int) + + +class TestToleranceConstants: + """Test convergence and tolerance threshold constants.""" + + def test_tolerance_positive(self) -> None: + """Test that tolerance values are positive.""" + assert DEFAULT_TOLERANCE > 0 + assert DEFAULT_CONVERGENCE_THRESHOLD > 0 + assert BARRIER_METHOD_MIN_MU > 0 + assert PENALTY_METHOD_TOLERANCE > 0 + + def test_tolerance_ordering(self) -> None: + """Test that stricter tolerances have smaller values.""" + assert BARRIER_METHOD_MIN_MU < DEFAULT_CONVERGENCE_THRESHOLD + assert DEFAULT_CONVERGENCE_THRESHOLD < DEFAULT_TOLERANCE + + +class TestNumericalStabilityConstants: + """Test numerical stability constants.""" + + def test_epsilon_values(self) -> None: + """Test epsilon values are appropriately small.""" + assert EPSILON_STABILITY > 0 + assert ADAM_EPSILON > 0 + assert EPSILON_STABILITY < 1e-6 + + def test_epsilon_ordering(self) -> None: + """Test epsilon values maintain expected relationships.""" + assert EPSILON_STABILITY == ADAM_EPSILON + + +class TestPSOConstants: + """Test Particle Swarm Optimization constants.""" + + def test_pso_coefficients_positive(self) -> None: + """Test PSO coefficients are positive.""" + assert PSO_INERTIA_WEIGHT > 0 + assert PSO_COGNITIVE_COEFFICIENT > 0 + assert PSO_SOCIAL_COEFFICIENT > 0 + + def test_pso_coefficients_reasonable(self) -> None: + """Test PSO coefficients are in reasonable ranges.""" + assert 0 < PSO_INERTIA_WEIGHT < 1 + assert 0 < PSO_COGNITIVE_COEFFICIENT < 5 + assert 0 < PSO_SOCIAL_COEFFICIENT < 5 + + +class TestAdamConstants: + """Test Adam-family optimizer constants.""" + + def test_adam_beta_ranges(self) -> None: + """Test Adam beta values are in valid ranges.""" + assert 0 < ADAM_BETA1 < 1 + assert 0 < ADAM_BETA2 < 1 + assert ADAM_BETA1 < ADAM_BETA2 + + def test_learning_rates_positive(self) -> None: + """Test learning rates are positive.""" + assert ADAMW_LEARNING_RATE > 0 + assert NADAM_LEARNING_RATE > 0 + + def test_learning_rates_reasonable(self) -> None: + """Test learning rates are in reasonable ranges.""" + assert ADAMW_LEARNING_RATE < 1 + assert NADAM_LEARNING_RATE < 1 + + def test_weight_decay_reasonable(self) -> None: + """Test weight decay is in reasonable range.""" + assert 0 < ADAMW_WEIGHT_DECAY < 1 + + +class TestAlgorithmSpecificConstants: + """Test algorithm-specific constants.""" + + def test_golden_ratio(self) -> None: + """Test golden ratio approximation.""" + expected_golden_ratio = (1 + 5**0.5) / 2 + assert abs(GOLDEN_RATIO - expected_golden_ratio) < 1e-10 + + def test_fraction_ranges(self) -> None: + """Test fraction constants are in valid ranges.""" + assert 0 < ELITE_FRACTION < 1 + + +class TestBoundConstants: + """Test bound constants for benchmark functions.""" + + def test_bounds_positive(self) -> None: + """Test bound constants are positive.""" + assert SHIFTED_ACKLEY_BOUND > 0 + assert ACKLEY_BOUND > 0 + assert SPHERE_BOUND > 0 + assert ROSENBROCK_BOUND > 0 + + def test_ackley_bounds_relationship(self) -> None: + """Test relationship between Ackley bound variants.""" + assert SHIFTED_ACKLEY_BOUND < ACKLEY_BOUND + + +class TestPowerConstants: + """Test power and exponent constants.""" + + def test_power_values(self) -> None: + """Test power constants are correct.""" + assert POWER_THIRTY_TWO == 32 + assert isinstance(POWER_THIRTY_TWO, int) + + +class TestConstantConsistency: + """Test consistency across related constants.""" + + def test_no_zero_constants(self) -> None: + """Test that no constants are exactly zero (except where intentional).""" + constants_to_check = [ + DEFAULT_POPULATION_SIZE, + DEFAULT_MAX_ITERATIONS, + DEFAULT_TOLERANCE, + PSO_INERTIA_WEIGHT, + ADAM_BETA1, + ADAM_BETA2, + ] + for constant in constants_to_check: + assert constant != 0 + + def test_constants_have_expected_types(self) -> None: + """Test constants have expected types.""" + # Integer constants + assert isinstance(DEFAULT_POPULATION_SIZE, int) + assert isinstance(DEFAULT_MAX_ITERATIONS, int) + assert isinstance(DEFAULT_SEED, int) + assert isinstance(POWER_THIRTY_TWO, int) + + # Float constants + assert isinstance(PSO_INERTIA_WEIGHT, float) + assert isinstance(ADAM_BETA1, float) + assert isinstance(DEFAULT_TOLERANCE, float) diff --git a/opt/test/test_demo.py b/opt/test/test_demo.py index 16fa6f6f..95059ac6 100644 --- a/opt/test/test_demo.py +++ b/opt/test/test_demo.py @@ -2,10 +2,7 @@ from __future__ import annotations -import pytest - from opt.benchmark.functions import rosenbrock -from opt.benchmark.functions import shifted_ackley from opt.benchmark.functions import sphere from opt.demo import run_demo from opt.swarm_intelligence.particle_swarm import ParticleSwarm @@ -14,21 +11,19 @@ def test_run_demo_basic(): """Test basic demo functionality.""" best_solution, best_fitness = run_demo(ParticleSwarm, max_iter=10) - + # Check return types assert best_solution is not None assert isinstance(best_fitness, float) - + # Check solution dimensions assert len(best_solution) == 2 def test_run_demo_custom_function(): """Test demo with custom function.""" - best_solution, best_fitness = run_demo( - ParticleSwarm, func=sphere, max_iter=10 - ) - + best_solution, best_fitness = run_demo(ParticleSwarm, func=sphere, max_iter=10) + assert best_solution is not None assert isinstance(best_fitness, float) @@ -43,7 +38,7 @@ def test_run_demo_custom_bounds(): dim=3, max_iter=10, ) - + assert len(best_solution) == 3 assert isinstance(best_fitness, float) @@ -51,12 +46,8 @@ def test_run_demo_custom_bounds(): def test_run_demo_with_kwargs(): """Test demo with additional optimizer kwargs.""" best_solution, best_fitness = run_demo( - ParticleSwarm, - max_iter=10, - population_size=20, - c1=2.0, - c2=2.0, + ParticleSwarm, max_iter=10, population_size=20, c1=2.0, c2=2.0 ) - + assert best_solution is not None assert isinstance(best_fitness, float) diff --git a/pyproject.toml b/pyproject.toml index 0b3c8fcc..d6d33dfe 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -77,32 +77,36 @@ preview = false # Set to all select = ["ALL"] ignore = [ - "PLR0913", - "PLR1704", - "N803", - "N806", - "E741", - "E501", - "T201", - "COM812", - "NPY002", # Legacy numpy random calls - used extensively in optimization algorithms - "D107", # Missing docstring in __init__ - "D103", # Missing docstring in public function - "PLR2004", # Magic value comparisons - common in optimization algorithms - "PLR0912", # Too many branches - complex algorithms need them - "PLR0915", # Too many statements - complex algorithms need them - "C901", # Too complex - optimization algorithms are inherently complex - "SIM109", # Use in tuple comparison - minor style - "SIM110", # Use all() generator - minor style - "PLR1714", # Consider merging comparisons - minor style - "PERF401", # Use list comprehension - minor style - "RET504", # Unnecessary assignment before return - minor style - "S112", # try-except-continue - acceptable pattern - "BLE001", # Blind exception - sometimes necessary - "B007", # Loop control variable not used - sometimes necessary - "B023", # Function doesn't bind loop variable - sometimes necessary - "PLC0415", # Import not at top level - sometimes necessary for optional deps - "F841", # Unused variable - sometimes necessary for clarity + "PLR0913", # Too many arguments - should use config objects + "PLR1704", # Redefining argument - needs refactor + "N803", # Argument name not lowercase + "N806", # Variable name not lowercase + "E741", # Ambiguous variable name + "E501", # Line too long + "T201", # Print statements - used in example scripts + "COM812", # Missing trailing comma + "NPY002", # Legacy numpy random calls - used extensively in optimization algorithms + "D107", # Missing docstring in __init__ + "D103", # Missing docstring in public function + # NOTE: PLR2004 (Magic value comparisons) is being gradually addressed. + # Constants module created (opt/constants.py) to centralize magic numbers. + # Core files (abstract_optimizer, particle_swarm, nadam, adamw) have been migrated. + # Remaining files will be migrated incrementally. See issue for progress. + "PLR2004", # Magic value comparisons - common in optimization algorithms + "PLR0912", # Too many branches - complex algorithms need them + "PLR0915", # Too many statements - complex algorithms need them + "C901", # Too complex - optimization algorithms are inherently complex + "SIM109", # Use in tuple comparison - minor style + "SIM110", # Use all() generator - minor style + "PLR1714", # Consider merging comparisons - minor style + "PERF401", # Use list comprehension - minor style + "RET504", # Unnecessary assignment before return - minor style + "S112", # try-except-continue - acceptable pattern + "BLE001", # Blind exception - sometimes necessary + "B007", # Loop control variable not used - sometimes necessary + "B023", # Function doesn't bind loop variable - sometimes necessary + "PLC0415", # Import not at top level - sometimes necessary for optional deps + "F841", # Unused variable - sometimes necessary for clarity ] # Allow fix for all enabled rules (when `--fix`) is provided.