117 lines
7.2 KiB
Python
117 lines
7.2 KiB
Python
import numpy as np # 引入NumPy库,用于高效的数值计算
|
||
from pySOT.utils import round_vars # 引入用于四舍五入的函数
|
||
from typing import TYPE_CHECKING # 引入类型检查工具
|
||
if TYPE_CHECKING:
|
||
from policy import Policy
|
||
|
||
class GeneticAlgorithm: # 定义一个遗传算法类
|
||
def __init__(self, the_policy: 'Policy'): # 初始化方法,接收一个 Policy 对象
|
||
self.n_variables = the_policy.dim # 从 policy 中获取问题的维度
|
||
self.lower_boundary = the_policy.lb # 获取决策变量的下界
|
||
self.upper_boundary = the_policy.ub # 获取决策变量的上界
|
||
self.integer_variables = the_policy.int_var # 获取整数变量的索引
|
||
|
||
self.sigma = 0.2 # 设置变异操作的标准差
|
||
self.p_mutation = 1.0 / the_policy.dim # 设置变异概率
|
||
self.tournament_size = 5 # 设置锦标赛选择的大小
|
||
self.p_cross = 0.9 # 设置交叉概率
|
||
|
||
pop_size = the_policy.arr_init_doe_points.shape[0] # 获取种群大小
|
||
self.lst_value = the_policy.lst_y_init_doe_points # 初始化每个个体的适应度值
|
||
|
||
# 如果种群大小是奇数,生成一个随机个体来确保种群大小是偶数
|
||
if pop_size % 2 == 1:
|
||
arr_random = np.random.rand(1, self.n_variables) # 生成一个随机的个体
|
||
arr_one_random = self.lower_boundary + arr_random * (self.upper_boundary - self.lower_boundary) # 将随机个体约束在边界内
|
||
self.lst_value.append(the_policy.eval(arr_one_random[0, :], is_init_points=True)) # 评估该个体的适应度
|
||
self.population = np.vstack((the_policy.arr_init_doe_points, arr_one_random)) # 将该个体加入到种群中
|
||
else:
|
||
self.population = np.copy(the_policy.arr_init_doe_points) # 直接使用初始种群
|
||
|
||
self.n_individuals = self.population.shape[0] # 获取种群中个体的数量
|
||
assert self.n_individuals == pop_size or self.n_individuals == pop_size + 1, 'Wrong pop size' # 确保种群大小正确
|
||
|
||
# 如果有整数变量,需要进行位置四舍五入
|
||
if len(self.integer_variables) > 0:
|
||
self.population[:, self.integer_variables] = np.round(self.population[:, self.integer_variables]) # 对整数变量四舍五入
|
||
for i in self.integer_variables:
|
||
ind = np.where(self.population[:, i] < self.lower_boundary[i]) # 如果超出了下界,修正为下界
|
||
self.population[ind, i] += 1
|
||
ind = np.where(self.population[:, i] > self.upper_boundary[i]) # 如果超出了上界,修正为上界
|
||
self.population[ind, i] -= 1
|
||
|
||
self.ind, self.best_individual, self.best_value = None, None, None # 初始化最优个体和最优值
|
||
self.pop_next, self.lst_pop_next_is_evaluated = None, None # 初始化下一代种群和评估标志
|
||
self.update_info() # 更新最优解信息
|
||
|
||
def update_info(self):
|
||
# 更新最优个体和适应度值
|
||
self.ind = np.argmin(self.lst_value) # 获取适应度最小的个体(假设目标是最小化)
|
||
self.best_individual = np.copy(self.population[self.ind, :]) # 复制最优个体
|
||
self.best_value = self.lst_value[self.ind] # 记录最优值
|
||
|
||
self.pop_next, self.lst_pop_next_is_evaluated = self._generate_next_population() # 生成下一代种群
|
||
self.lst_value = [] # 清空当前种群的适应度值
|
||
|
||
def _generate_next_population(self):
|
||
# 生成下一代种群
|
||
competitors = np.random.randint(0, self.n_individuals, (self.n_individuals, self.tournament_size)) # 随机选择竞赛个体
|
||
ind = np.argmin(np.array(self.lst_value)[competitors], axis=1) # 选择每轮锦标赛中的最优个体
|
||
winner_indices = np.zeros(self.n_individuals, dtype=int) # 用于存储胜利个体的索引
|
||
for i in range(self.tournament_size): # 进行锦标赛选择
|
||
winner_indices[np.where(ind == i)] = competitors[np.where(ind == i), i]
|
||
|
||
# 按照锦标赛结果将种群分为父母
|
||
parent1 = self.population[winner_indices[0: self.n_individuals // 2], :]
|
||
parent2 = self.population[winner_indices[self.n_individuals // 2: self.n_individuals], :]
|
||
|
||
# 交叉操作:对父母个体进行交叉
|
||
cross = np.where(np.random.rand(self.n_individuals // 2) < self.p_cross)[0] # 按照概率决定哪些个体进行交叉
|
||
nn = len(cross) # 计算交叉个体的数量
|
||
alpha = np.random.rand(nn, 1) # 生成交叉系数
|
||
|
||
# 创建新的染色体
|
||
parent1_new = np.multiply(alpha, parent1[cross, :]) + np.multiply(1 - alpha, parent2[cross, :])
|
||
parent2_new = np.multiply(alpha, parent2[cross, :]) + np.multiply(1 - alpha, parent1[cross, :])
|
||
parent1[cross, :] = parent1_new
|
||
parent2[cross, :] = parent2_new
|
||
arr_new_population = np.concatenate((parent1, parent2)) # 合并两个父代得到新的种群
|
||
|
||
# 变异操作
|
||
scale_factors = self.sigma * (self.upper_boundary - self.lower_boundary) # 计算变异的尺度
|
||
perturbation = np.random.randn(self.n_individuals, self.n_variables) # 生成扰动
|
||
perturbation = np.multiply(perturbation, scale_factors) # 根据尺度调整扰动
|
||
perturbation = np.multiply(
|
||
perturbation, (np.random.rand(self.n_individuals, self.n_variables) < self.p_mutation)
|
||
) # 根据变异概率决定哪些位置进行扰动
|
||
|
||
arr_new_population += perturbation # 将扰动添加到新种群
|
||
arr_new_population = np.maximum(np.reshape(self.lower_boundary, (1, self.n_variables)), arr_new_population) # 确保不超过下界
|
||
arr_new_population = np.minimum(np.reshape(self.upper_boundary, (1, self.n_variables)), arr_new_population) # 确保不超过上界
|
||
|
||
# 如果有整数变量,进行四舍五入
|
||
if len(self.integer_variables) > 0:
|
||
arr_new_population = round_vars(arr_new_population, self.integer_variables, self.lower_boundary,
|
||
self.upper_boundary)
|
||
|
||
assert arr_new_population.shape[0] == self.n_individuals, 'Wrong arr_new_population shape' # 确保新种群的大小正确
|
||
return arr_new_population, [False] * self.n_individuals # 返回新种群和评估标志(都设为未评估)
|
||
|
||
def select_next_point(self):
|
||
# 选择下一个要评估的个体
|
||
for idx_ind, is_evaluated in enumerate(self.lst_pop_next_is_evaluated):
|
||
if not is_evaluated: # 如果该个体没有被评估
|
||
return self.pop_next[idx_ind, :] # 返回该个体
|
||
|
||
def receive_sim_value(self, the_value):
|
||
# 接收评估结果,并更新适应度信息
|
||
self.lst_value.append(the_value) # 将评估值添加到适应度列表
|
||
idx_ind = 0
|
||
for idx_ind, is_evaluated in enumerate(self.lst_pop_next_is_evaluated):
|
||
if not is_evaluated: # 找到未评估的个体
|
||
self.lst_pop_next_is_evaluated[idx_ind] = True # 标记该个体为已评估
|
||
break
|
||
if idx_ind == len(self.lst_pop_next_is_evaluated) - 1:
|
||
assert idx_ind == self.n_individuals - 1, 'Wrong index' # 确保所有个体都已评估
|
||
self.update_info() # 更新最优解信息
|