Differential Evolution Algorithm: Python Implementation and Detailed Explanation

Click the blue text above to follow us

Differential Evolution Algorithm: Python Implementation and Detailed Explanation

The Differential Evolution (DE) algorithm is a population-based global optimization algorithm proposed by Rainer Storn and Kenneth Price in 1995. It is suitable for solving nonlinear, multimodal, and high-dimensional optimization problems, with advantages such as simple implementation, few parameters, and fast convergence speed.

Detailed Algorithm Steps

1. Initialize Parameters and Population

The Differential Evolution algorithm first needs to initialize some key parameters:

Population Size (pop_size): Determines the number of individuals in the population, usually set to 5-10 times the problem dimension.

Mutation Factor (F): A factor that controls the mutation amplitude, typically ranging from 0.4 to 1.0.

Crossover Probability (CR): Determines the probability of mixing the mutated and current individual information, usually set between 0.7 and 0.9.

Maximum Iterations (max_iter): One of the termination conditions for the algorithm.

When initializing the population, the initial population is randomly generated within the range [0,1], and then scaled to the actual problem boundary. The fitness value of each individual is calculated, and the best individual is recorded.

# Randomly generate the initial population within the range [0,1]self.population = np.random.rand(self.pop_size, self.dim)# Scale the population to the actual boundary rangeself.population = self.min_b + self.population * self.diff# Calculate initial fitnessself.fitness = np.array([self.objective_func(ind) for ind in self.population])

2. Mutation Operation

The mutation operation is one of the core steps of the Differential Evolution algorithm, generating new mutated individuals based on the differences among individuals in the current population. Common mutation strategies include DE/rand/1, DE/best/1, etc.

In our implementation, we use the DE/rand/1 strategy:

v_i = x_r1 + F * (x_r2 – x_r3)

After the mutation operation, we need to ensure that the mutated individuals do not exceed the problem’s boundary:

# Boundary handling: Ensure that the mutated individuals are within the boundary rangemutants[i] = np.clip(mutants[i], self.min_b, self.max_b)

3. Crossover Operation

The crossover operation mixes the mutated individuals with the current individuals to generate trial individuals. We use the binomial crossover strategy:

# Randomly select at least one dimension for crossovercross_points = np.random.rand(self.dim) < self.CRif not np.any(cross_points): cross_points[np.random.randint(0, self.dim)] = True# Perform crossovertrials[i] = np.where(cross_points, mutants[i], self.population[i])

4. Selection Operation

The selection operation adopts a “greedy” strategy, comparing the fitness of trial individuals and original individuals, retaining the better individuals:

# Compare the fitness of trial individuals and original individualsimproved = trial_fitness < self.fitnessself.population[improved] = trials[improved]self.fitness[improved] = trial_fitness[improved]

5. Update Global Optimal Solution

# Update global optimal solutioncurrent_best_idx = np.argmin(self.fitness)if self.fitness[current_best_idx] < self.best_fitness: self.best_solution = self.population[current_best_idx] self.best_fitness = self.fitness[current_best_idx]

The complete code is as follows:

import numpy as npimport matplotlib.pyplot as pltclass DifferentialEvolution: “”” Differential Evolution Algorithm Implementation Class Parameters: objective_func: Objective function bounds: List of boundaries for each variable, e.g.,[(min1, max1), (min2, max2), …] pop_size: Population size(default 20) F: Mutation factor(default 0.8) CR: Crossover probability(default 0.7) max_iter: Maximum number of iterations(default 1000) verbose: Whether to print iteration information(default False) “”” def __init__(self, objective_func, bounds, pop_size=20, F=0.8, CR=0.7, max_iter=1000, verbose=False): self.objective_func = objective_func self.bounds = np.array(bounds) self.pop_size = pop_size self.F = F self.CR = CR self.max_iter = max_iter self.verbose = verbose self.dim = len(bounds) # Problem dimension self.min_b, self.max_b = np.asarray(bounds).T # Extract boundaries self.diff = np.fabs(self.min_b – self.max_b) # Calculate boundary difference # Initialize population and fitness self.population = None self.fitness = None self.best_solution = None self.best_fitness = float(‘inf’) self.history = [] # Record optimal fitness history def initialize_population(self): “””Initialize population“”” # Randomly generate the initial population within the range [0,1] self.population = np.random.rand(self.pop_size, self.dim) # Scale the population to the actual boundary range self.population = self.min_b + self.population * self.diff # Calculate initial fitness self.fitness = np.array([self.objective_func(ind) for ind in self.population]) # Record the best individual best_idx = np.argmin(self.fitness) self.best_solution = self.population[best_idx] self.best_fitness = self.fitness[best_idx] self.history.append(self.best_fitness) def mutate(self): “””Mutation operation(UsingDE/rand/1strategy)””” mutants = np.zeros_like(self.population) for i in range(self.pop_size): # Randomly select three individuals different from the current individual idxs = [idx for idx in range(self.pop_size) if idx != i] a, b, c = self.population[np.random.choice(idxs, 3, replace=False)] # Generate mutated individuals(Mutation operation) mutants[i] = a + self.F * (b – c) # Boundary handling: Ensure that the mutated individuals are within the boundary range mutants[i] = np.clip(mutants[i], self.min_b, self.max_b) return mutants def crossover(self, mutants): “””Crossover operation(Binomial crossover)””” trials = np.zeros_like(self.population) for i in range(self.pop_size): # Randomly select at least one dimension for crossover cross_points = np.random.rand(self.dim) < self.CR if not np.any(cross_points): cross_points[np.random.randint(0, self.dim)] = True # Perform crossover trials[i] = np.where(cross_points, mutants[i], self.population[i]) return trials def select(self, trials): “””Select operation“”” trial_fitness = np.array([self.objective_func(ind) for ind in trials]) # Compare the fitness of trial individuals and original individuals improved = trial_fitness < self.fitness self.population[improved] = trials[improved] self.fitness[improved] = trial_fitness[improved] # Update global optimal solution current_best_idx = np.argmin(self.fitness) if self.fitness[current_best_idx] < self.best_fitness: self.best_solution = self.population[current_best_idx] self.best_fitness = self.fitness[current_best_idx] self.history.append(self.best_fitness) def evolve(self): “””Execute evolution process“”” self.initialize_population() for iter in range(self.max_iter): mutants = self.mutate() # Mutation trials = self.crossover(mutants) # Cross self.select(trials) # Select if self.verbose and (iter % 100 == 0 or iter == self.max_iter – 1): print(f”Iteration {iter: Best Fitness = {self.best_fitness:.6f}”) return self.best_solution, self.best_fitness def plot_convergence(self): “””Plot convergence curve“”” plt.figure(figsize=(10, 6)) plt.plot(self.history, ‘b-‘, linewidth=2) plt.title(‘Convergence Curve’, fontsize=14) plt.xlabel(‘Iteration’, fontsize=12) plt.ylabel(‘Best Fitness’, fontsize=12) plt.grid(True) plt.show()# Example: Test the Differential Evolution Algorithmif __name__ == “__main__”: # Define the objective function(Here we use theRastriginfunction) def rastrigin(x): “””Rastriginfunction: Multimodal function, commonly used to test optimization algorithms“”” A = 10 return A * len(x) + sum([(xi ** 2 – A * np.cos(2 * np.pi * xi)) for xi in x]) # Define variable boundaries(5dimensional problem) bounds = [(-5.12, 5.12)] * 5 # Create an instance of Differential Evolution de = DifferentialEvolution(rastrigin, bounds, pop_size=50, F=0.8, CR=0.9, max_iter=1000, verbose=True) # Execute optimization best_solution, best_fitness = de.evolve() # Output results print(\nOptimization Results:”) print(f”Best Solution: {best_solution}”) print(f”Best Fitness: {best_fitness:.6f}”) # Plot convergence curve de.plot_convergence()

Using the Rastrigin function for testing, the results are as follows:

Differential Evolution Algorithm: Python Implementation and Detailed ExplanationDifferential Evolution Algorithm: Python Implementation and Detailed Explanation

END

Leave a Comment