High-performance, modular Differential Evolution optimization
Project description
PyRADE
Python Rapid Algorithm for Differential Evolution
A high-performance, modular Differential Evolution (DE) optimization package that demonstrates how clean OOP architecture can outperform monolithic implementations through aggressive vectorization.
๐ What is PyRADE?
PyRADE is a production-ready optimization library implementing Differential Evolution (DE), a powerful evolutionary algorithm for global optimization. Unlike traditional implementations that sacrifice code quality for performance, PyRADE proves you can have both through intelligent design.
Why Choose PyRADE?
โ
3-5x Faster than typical DE implementations
โ
Clean, Modular Code that's easy to understand and extend
โ
Battle-Tested Algorithms with 10+ benchmark functions included
โ
Flexible Architecture - plug in your own strategies with ease
โ
Production Ready - comprehensive documentation and examples
๐ Key Features
- โก High Performance: 3-5x faster than traditional implementations through aggressive NumPy vectorization
- ๐๏ธ Clean Architecture: Strategy pattern for all operators - easy to understand and extend
- ๐ง Modular Design: Plug-and-play mutation, crossover, and selection strategies
- ๐ฆ Production Ready: Well-documented, tested, professional-quality code
- ๐ฏ Easy to Use: Simple, intuitive API similar to scikit-learn optimizers
- ๐งช Comprehensive: Includes 10+ benchmark functions and multiple real-world examples
- ๐ฌ Extensible: Create custom strategies in minutes, not hours
๐ Performance
PyRADE's vectorized implementation significantly outperforms traditional loop-based DE:
| Function | Dimension | Modular (PyRADE) | Monolithic | Speedup |
|---|---|---|---|---|
| Sphere | 20 | 0.45s | 1.89s | 4.2x |
| Rastrigin | 20 | 0.52s | 2.14s | 4.1x |
| Rosenbrock | 20 | 0.48s | 1.95s | 4.1x |
| Ackley | 20 | 0.51s | 2.08s | 4.1x |
Average speedup: 4.1x without sacrificing code quality!
๐ฆ Installation
# Clone the repository
git clone https://github.com/yourusername/pyrade.git
cd pyrade
# Install dependencies
pip install -r requirements.txt
# Install package
pip install -e .
Or install directly:
pip install numpy>=1.20.0
๐ฏ Quick Start
Example 1: Minimizing a Simple Function
Let's start by minimizing the classic Sphere function: f(x) = ฮฃxยฒ
import numpy as np
from pyrade import DifferentialEvolution
# Define your objective function to minimize
def sphere(x):
"""Simple quadratic function - global minimum at origin"""
return np.sum(x**2)
# Create the optimizer
optimizer = DifferentialEvolution(
objective_func=sphere,
bounds=[(-100, 100)] * 10, # 10-dimensional problem, each dimension in [-100, 100]
pop_size=50, # Population size (recommended: 5-10x dimensions)
max_iter=200, # Maximum iterations
verbose=True, # Show progress
seed=42 # For reproducibility
)
# Run the optimization
result = optimizer.optimize()
# View results
print(f"Best solution found: {result['best_solution']}")
print(f"Best fitness value: {result['best_fitness']:.6e}")
print(f"Optimization time: {result['time']:.2f}s")
Output:
Final best fitness: 6.834298e+01
Total time: 0.050s
Example 2: Using Built-in Benchmark Functions
PyRADE includes many standard test functions. Let's optimize the challenging Rastrigin function:
from pyrade import DifferentialEvolution
from pyrade.benchmarks import Rastrigin
# Create a 20-dimensional Rastrigin function (highly multimodal!)
func = Rastrigin(dim=20)
print(f"Global optimum: {func.optimum}")
print(f"Search bounds: {func.bounds}")
# Optimize using default settings
optimizer = DifferentialEvolution(
objective_func=func,
bounds=func.get_bounds_array(), # Get properly formatted bounds
pop_size=100,
max_iter=300,
verbose=True
)
result = optimizer.optimize()
# Check how close we got to the global optimum
error = abs(result['best_fitness'] - func.optimum)
print(f"\nFinal fitness: {result['best_fitness']:.6e}")
print(f"Error from global optimum: {error:.6e}")
print(f"Success: {error < 1e-3}")
Available Benchmark Functions:
Sphere,Rastrigin,Rosenbrock,Ackley,GriewankSchwefel,Levy,Michalewicz,Zakharov
Example 3: Using Custom Strategies
Fine-tune the algorithm by selecting specific strategies:
from pyrade import DifferentialEvolution
from pyrade.operators import DEbest1, ExponentialCrossover, GreedySelection
from pyrade.benchmarks import Ackley
func = Ackley(dim=30)
# Use exploitative mutation for faster convergence
optimizer = DifferentialEvolution(
objective_func=func,
bounds=func.get_bounds_array(),
mutation=DEbest1(F=0.8), # Exploitative mutation strategy
crossover=ExponentialCrossover(CR=0.9), # Exponential crossover
selection=GreedySelection(), # Greedy selection
pop_size=100,
max_iter=500,
verbose=True
)
result = optimizer.optimize()
print(f"Optimized fitness: {result['best_fitness']:.6e}")
Strategy Guide:
- Mutation:
DErand1(exploration),DEbest1(exploitation),DEcurrentToBest1(balanced) - Crossover:
BinomialCrossover(standard),ExponentialCrossover(segment-based) - Selection:
GreedySelection(standard),TournamentSelection(diversity),ElitistSelection(elite preservation)
๐๏ธ Architecture
PyRADE uses a clean, extensible architecture based on the Strategy pattern:
pyrade/
โโโ core/
โ โโโ algorithm.py # Main DifferentialEvolution class
โ โโโ population.py # Population management
โโโ operators/
โ โโโ mutation.py # Mutation strategies (DE/rand/1, DE/best/1, etc.)
โ โโโ crossover.py # Crossover strategies (Binomial, Exponential)
โ โโโ selection.py # Selection strategies (Greedy, Tournament, etc.)
โโโ utils/
โ โโโ boundary.py # Boundary handling (Clip, Reflect, Random, etc.)
โ โโโ termination.py # Termination criteria
โโโ benchmarks/
โโโ functions.py # Standard test functions
๐จ Available Strategies
Mutation Strategies
- DE/rand/1: Most common, good exploration
- DE/best/1: Exploitative, fast convergence
- DE/current-to-best/1: Balanced exploration/exploitation
- DE/rand/2: More exploratory with two difference vectors
Crossover Strategies
- Binomial: Standard independent dimension crossover
- Exponential: Contiguous segment crossover
- Uniform: Equal probability crossover
Selection Strategies
- Greedy: Keep better individual (standard)
- Tournament: Tournament-based selection
- Elitist: Preserve top individuals
Boundary Handlers
- Clip: Clip to bounds (most common)
- Reflect: Reflect at boundaries
- Random: Replace with random value
- Wrap: Toroidal topology
- Midpoint: Use midpoint between bound and parent
๐ Benchmark Functions
PyRADE includes 10+ standard test functions:
- Sphere: Simple unimodal
- Rastrigin: Highly multimodal
- Rosenbrock: Valley-shaped
- Ackley: Many local minima
- Griewank: Multimodal
- Schwefel: Deceptive
- Levy: Multimodal
- Michalewicz: Steep valleys
- Zakharov: Unimodal
Example 4: Real-World Application - Engineering Design
Optimize a real engineering problem with constraints:
import numpy as np
from pyrade import DifferentialEvolution
def pressure_vessel_cost(x):
"""
Minimize cost of a pressure vessel design.
x[0]: shell thickness, x[1]: head thickness
x[2]: inner radius, x[3]: length
"""
# Material and welding costs
cost = (
0.6224 * x[0] * x[2] * x[3] +
1.7781 * x[1] * x[2]**2 +
3.1661 * x[0]**2 * x[3] +
19.84 * x[0]**2 * x[2]
)
# Add penalty for constraint violations
penalty = 0
# Constraint: minimum shell thickness
if x[0] < 0.0625:
penalty += 1000 * (0.0625 - x[0])**2
# Constraint: minimum head thickness
if x[1] < 0.0625:
penalty += 1000 * (0.0625 - x[1])**2
# Constraint: minimum volume
volume = (np.pi * x[2]**2 * x[3] +
4/3 * np.pi * x[2]**3)
if volume < 1296000:
penalty += 10 * (1296000 - volume)**2
return cost + penalty
# Define bounds for each variable
bounds = [
(0.0625, 99), # shell thickness
(0.0625, 99), # head thickness
(10, 200), # inner radius
(10, 200) # length
]
optimizer = DifferentialEvolution(
objective_func=pressure_vessel_cost,
bounds=bounds,
pop_size=40,
max_iter=500,
seed=42
)
result = optimizer.optimize()
print(f"Optimal design cost: ${result['best_fitness']:.2f}")
print(f"Design parameters: {result['best_solution']}")
Example 5: Using Callbacks for Progress Monitoring
Track optimization progress with custom callbacks:
from pyrade import DifferentialEvolution
from pyrade.benchmarks import Rosenbrock
# Storage for tracking progress
history = {'iterations': [], 'fitness': []}
def progress_callback(iteration, best_fitness, best_solution):
"""Called after each iteration"""
history['iterations'].append(iteration)
history['fitness'].append(best_fitness)
# Print every 50 iterations
if iteration % 50 == 0:
print(f"Iteration {iteration:4d}: Best fitness = {best_fitness:.6e}")
func = Rosenbrock(dim=10)
optimizer = DifferentialEvolution(
objective_func=func,
bounds=func.get_bounds_array(),
pop_size=50,
max_iter=300,
callback=progress_callback, # Add your callback
verbose=False
)
result = optimizer.optimize()
# Plot convergence curve
import matplotlib.pyplot as plt
plt.plot(history['iterations'], history['fitness'])
plt.xlabel('Iteration')
plt.ylabel('Best Fitness')
plt.yscale('log')
plt.title('Convergence Curve')
plt.show()
๐ฌ Complete Examples
The examples/ directory contains comprehensive, ready-to-run examples:
basic_usage.py- Simple optimization scenarios with detailed explanationscustom_strategy.py- Creating and using custom mutation/crossover strategiesbenchmark_comparison.py- Performance benchmarking against monolithic implementations
Run examples:
cd examples
python basic_usage.py
python custom_strategy.py
python benchmark_comparison.py
What you'll learn:
- How to optimize different types of functions
- Using callbacks for monitoring
- Handling constraints with penalties
- Comparing different strategies
- Creating custom operators
- Performance optimization techniques
๐งช Experiment Manager & Output Structure
PyRADE includes a powerful ExperimentManager for automated benchmarking, visualization, and data export.
How It Works
- Selects and runs multiple benchmark functions with configurable parameters (runs, population, iterations, dimensions)
- Automatically generates and saves:
- Convergence plots (per function and combined)
- Fitness boxplots
- Raw data (NumPy arrays, CSVs)
- Summary statistics and rankings
- Timestamped experiment folders for easy organization
Output Folder Structure
experiment_YYYY-MM-DD_HH-MM-SS/
โโโ convergence_plots/
โ โโโ sphere_convergence.png
โ โโโ rastrigin_convergence.png
โ โโโ ... (one per function)
โโโ all_functions_convergence.png
โโโ fitness_boxplot.png
โโโ statistics.txt
โโโ csv_exports/
โ โโโ sphere_detailed.csv
โ โโโ summary_statistics.csv
โ โโโ ... (per function)
โโโ raw_data/
โ โโโ sphere_convergence.npy
โ โโโ sphere_final_fitness.npy
โ โโโ ... (per function)
โโโ config.json
Example Usage
from pyrade import ExperimentManager
manager = ExperimentManager(
benchmarks=['Sphere', 'Rastrigin', 'Rosenbrock'],
n_runs=30,
population_size=50,
max_iterations=100,
dimensions=10
)
manager.run_complete_pipeline()
All results are saved in a new folder named with the date and time of the experiment.
๐ Creating Custom Strategies
Custom Mutation Strategy
from pyrade.operators import MutationStrategy
import numpy as np
class MyMutation(MutationStrategy):
def __init__(self, F=0.8):
self.F = F
def apply(self, population, fitness, best_idx, target_indices):
pop_size = len(population)
# Your vectorized mutation logic here
# Must return mutants array of shape (pop_size, dim)
mutants = ... # Your implementation
return mutants
Custom Crossover Strategy
from pyrade.operators import CrossoverStrategy
import numpy as np
class MyCrossover(CrossoverStrategy):
def __init__(self, CR=0.9):
self.CR = CR
def apply(self, population, mutants):
# Your vectorized crossover logic here
# Must return trials array of shape (pop_size, dim)
trials = ... # Your implementation
return trials
๐ Performance Tips
- Use vectorized operations: All strategies should process entire population at once
- Tune population size: Typically 5-10x the problem dimension
- Choose appropriate F and CR: F=0.8, CR=0.9 work well for most problems
- Select mutation strategy wisely:
- DE/rand/1: General-purpose
- DE/best/1: Fast convergence on unimodal
- DE/current-to-best/1: Balanced approach
๐ค Contributing
Contributions are welcome! Areas for contribution:
- Additional mutation/crossover strategies
- More benchmark functions
- Performance optimizations
- Documentation improvements
- Bug fixes
๐ License
This project is licensed under the MIT License - see the LICENSE file for details.
๐ Acknowledgments
- Storn & Price for the original Differential Evolution algorithm
- NumPy team for the amazing numerical computing library
- Scientific Python community
๐ Contact
For questions, suggestions, or issues:
- Open an issue on GitHub
- Email: arartawil@gmail.com
๐ Star History
If you find PyRADE useful, please consider starring the repository!
PyRADE - Proving that clean, modular design and high performance can coexist! ๐
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pyrade-0.3.0.tar.gz.
File metadata
- Download URL: pyrade-0.3.0.tar.gz
- Upload date:
- Size: 3.4 MB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b19ea274f335221b9f6286e398e90cba5c81a0bbf329f450fb17288f87f0a065
|
|
| MD5 |
c56cbdad26c236f740f59f9e86a93e45
|
|
| BLAKE2b-256 |
66b504621903910f766c9a4a56df161f5c4b3e0e25e9cc733a29af064f525464
|
File details
Details for the file pyrade-0.3.0-py3-none-any.whl.
File metadata
- Download URL: pyrade-0.3.0-py3-none-any.whl
- Upload date:
- Size: 52.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
62ab3c2dacd2b85b21c9af84c5f4ee0cc293cf8b6b5063345353a88932e740f6
|
|
| MD5 |
050a5dba03d98a5fcbb54e5cea8978a2
|
|
| BLAKE2b-256 |
7737c4a2ccf719c614e9d88e1d0fe51e9a84c6179c0058e57429abb065d19afd
|