# Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License").
# You may not use this file except in compliance with the License.
# A copy of the License is located at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# or in the "license" file accompanying this file. This file is distributed
# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
# express or implied. See the License for the specific language governing
# permissions and limitations under the License.
import numpy as np
[docs]
class DifferentialevolutionOptimizer:
def __init__(self, f, lower, upper, fevals, strategy="best1", bin=1):
self.f = f
self.lower_bound = lower
self.upper_bound = upper
self.mut = 0.5
self.crossp = 0.5
self.popsize = 10
self.its = fevals // self.popsize - 1
self.dimensions = len(self.lower_bound)
self.de_pop = []
self.fitness = []
self.fbest = np.float("inf")
self.idxbest = 1
self.strategy = strategy
self.bin = bin
[docs]
def evolve(self, j):
best_idv = self.de_pop[self.idxbest]
current_idv = self.de_pop[j]
# perform mutation operation
if self.strategy == "rand1":
idxs = [idx for idx in range(self.popsize) if idx != j]
r1, r2, r3 = self.de_pop[np.random.choice(idxs, 3, replace=False)]
# Step 3.1: Perform mutation and checking
temp = r1 + self.mut * (r2 - r3)
vi = np.clip(temp, self.lower_bound, self.upper_bound)
if self.strategy == "best1":
idxs = [idx for idx in range(self.popsize) if idx != j]
r1, r2 = self.de_pop[np.random.choice(idxs, 2, replace=False)]
temp = best_idv + self.mut * (r1 - r2)
vi = np.clip(temp, self.lower_bound, self.upper_bound)
if self.strategy == "rand2":
idxs = [idx for idx in range(self.popsize) if idx != j]
r1, r2, r3, r4, r5 = self.de_pop[np.random.choice(idxs, 5, replace=False)]
temp = r1 + self.mut * (r1 - r2) + self.mut * (r3 - r4)
vi = np.clip(temp, self.lower_bound, self.upper_bound)
if self.strategy == "best2":
idxs = [idx for idx in range(self.popsize) if idx != j]
r1, r2, r3, r4 = self.de_pop[np.random.choice(idxs, 4, replace=False)]
temp = best_idv + self.mut * (r1 - r2) + self.mut * (r3 - r4)
vi = np.clip(temp, self.lower_bound, self.upper_bound)
if self.strategy == "currenttobest1":
idxs = [idx for idx in range(self.popsize) if idx != j]
r1, r2 = self.de_pop[np.random.choice(idxs, 2, replace=False)]
temp = (
current_idv + self.mut * (best_idv - current_idv) + self.mut * (r1 - r2)
)
vi = np.clip(temp, self.lower_bound, self.upper_bound)
if self.strategy == "randtobest1":
idxs = [idx for idx in range(self.popsize) if idx != j]
r1, r2, r3 = self.de_pop[np.random.choice(idxs, 3, replace=False)]
temp = r1 + self.mut * (best_idv - r1) + self.mut * (r2 - r3)
vi = np.clip(temp, self.lower_bound, self.upper_bound)
# perform crossover operation
if self.bin == 1:
cross_points = np.random.rand(self.dimensions) < self.crossp
if not np.any(cross_points):
cross_points[np.random.randint(0, self.dimensions)] = True
ui = np.where(cross_points, vi, current_idv)
else:
i = 0
ui = []
fill_point = np.random.randint(0, self.dimensions)
while i < self.dimensions and np.random.rand(0, 1) < self.crossp:
ui[fill_point] = vi[fill_point]
fill_point = (fill_point + 1) % self.dimensions
i += 1
return ui
[docs]
def run(self):
traj = []
# Step1: initialization
rand_temp = np.random.rand(self.popsize, self.dimensions)
diff = np.fabs(self.lower_bound - self.upper_bound)
self.de_pop = self.lower_bound + rand_temp * diff
# Step 2: population evaluation
best = None
for j in range(self.popsize):
ftemp = self.f(self.de_pop[j])
self.fitness.append(ftemp)
if ftemp < self.fbest:
self.fbest = ftemp
self.idxbest = j
best = self.de_pop[j]
traj.append(self.fbest)
# Step 3: Start evolutionary search
for i in range(self.its):
for j in range(self.popsize):
ui = self.evolve(j)
fit = self.f(ui)
# Step3.5: Perform Selection
if fit < self.fitness[j]:
self.fitness[j] = fit
self.de_pop[j] = ui
if fit < self.fitness[self.idxbest]:
self.idxbest = j
best = ui
traj.append(self.fitness[self.idxbest])
return best, traj