优化器类的定义和实现
This commit is contained in:
535
opt/smm/hybrid_genetic.py
Normal file
535
opt/smm/hybrid_genetic.py
Normal file
@@ -0,0 +1,535 @@
|
||||
from opt.smm.basis import *
|
||||
from opt.utils import *
|
||||
|
||||
|
||||
class HybridGeneticOpt(BaseOpt):
|
||||
def __init__(self, config, part_data, step_data, feeder_data=None):
|
||||
super().__init__(config, part_data, step_data, feeder_data)
|
||||
|
||||
self.part_nozzle = defaultdict(str)
|
||||
self.part_point_pos = defaultdict(list)
|
||||
|
||||
self.designated_nozzle = [''] * self.config.head_num
|
||||
self.designated_slot = [None] * self.config.slot_num
|
||||
|
||||
self.pickup_group = [] # pair_group: pickups from same initial group
|
||||
self.pickup_group_cycle = []
|
||||
self.pair_group = []
|
||||
self.feeder_part_arrange = defaultdict(list)
|
||||
|
||||
def pickup_group_combine(self, supply, supply_cycle, demand, demand_cycle):
|
||||
|
||||
combination, combination_cycle = demand.copy(), demand_cycle.copy()
|
||||
supply_cpy = supply.copy()
|
||||
|
||||
while True:
|
||||
supply_cpy_bits = self.config.head_num - supply_cpy.count(None)
|
||||
if supply_cpy_bits == 0:
|
||||
break
|
||||
max_match_offset, max_match_counter = 0, 0
|
||||
supply_cpy_index = [idx for idx, part in enumerate(supply_cpy) if part] # 加快搜索速度
|
||||
for offset in range(-supply_cpy_index[-1], self.config.head_num - supply_cpy_index[0]):
|
||||
match_counter = 0
|
||||
for idx, part in enumerate(supply_cpy):
|
||||
if 0 <= idx + offset < self.config.head_num:
|
||||
if part is None:
|
||||
continue
|
||||
if combination[idx + offset] is None and self.designated_nozzle[idx + offset] == self.designated_nozzle[idx]:
|
||||
match_counter += 1
|
||||
if match_counter > max_match_counter:
|
||||
max_match_counter = match_counter
|
||||
max_match_offset = offset
|
||||
if match_counter == supply_cpy_bits:
|
||||
break
|
||||
|
||||
for idx, part in enumerate(supply_cpy):
|
||||
if 0 <= idx + max_match_offset < self.config.head_num:
|
||||
if part is None:
|
||||
continue
|
||||
|
||||
if demand[idx + max_match_offset] is None:
|
||||
combination[idx + max_match_offset] = part
|
||||
combination_cycle[idx + max_match_offset] = supply_cycle[idx]
|
||||
supply_cpy[idx] = None
|
||||
|
||||
if max_match_counter == 0:
|
||||
break
|
||||
|
||||
return combination, combination_cycle
|
||||
|
||||
def cal_ind_val(self, individual):
|
||||
|
||||
place_time, pick_time = 0.234, 0.4
|
||||
x_moving_speed, y_moving_speed = 300, 300 # mm/s
|
||||
|
||||
prev_pair_index = None
|
||||
sequenced_pickup_group, sequenced_pickup_cycle = [], []
|
||||
for gene in individual:
|
||||
pickup = self.pickup_group[gene]
|
||||
|
||||
pair_index = None
|
||||
for idx, pair in enumerate(self.pair_group):
|
||||
if gene in pair:
|
||||
pair_index = idx
|
||||
break
|
||||
|
||||
if pair_index is not None and pair_index == prev_pair_index:
|
||||
for idx, component in enumerate(pickup):
|
||||
sequenced_pickup_group[-1][idx] = component
|
||||
else:
|
||||
sequenced_pickup_group.append(pickup.copy())
|
||||
sequenced_pickup_cycle.append(self.pickup_group_cycle[gene])
|
||||
|
||||
V = [float('inf') for _ in range(len(sequenced_pickup_group) + 1)] # Node Value
|
||||
V[0] = 0
|
||||
V_SNode = [-1 for _ in range(len(sequenced_pickup_group) + 1)]
|
||||
|
||||
nozzle_assigned_heads = defaultdict(int)
|
||||
for nozzle in self.designated_nozzle:
|
||||
nozzle_assigned_heads[nozzle] += 1
|
||||
|
||||
pickup_result, pickup_cycle_result = [[] for _ in range(len(V))], [[] for _ in range(len(V))]
|
||||
component_point_index = defaultdict(int)
|
||||
|
||||
intv_ratio = self.config.head_intv // self.config.head_intv
|
||||
for i in range(1, len(V)):
|
||||
cost, t0 = 0, 0
|
||||
load = defaultdict(int)
|
||||
Pd, Pd_cycle = [None for _ in range(self.config.head_num)], [0 for _ in range(self.config.head_num)] # demand pickup
|
||||
j = i
|
||||
while j < len(V):
|
||||
Ps, Ps_cycle = sequenced_pickup_group[j - 1], [sequenced_pickup_cycle[j - 1] for _ in
|
||||
range(self.config.head_num)] # supply pickup and its cycle
|
||||
for part in Ps:
|
||||
if part:
|
||||
load[self.part_nozzle[part]] += 1
|
||||
|
||||
is_combinable = True
|
||||
for nozzle, counter in load.items():
|
||||
if counter > nozzle_assigned_heads[nozzle]:
|
||||
is_combinable = False
|
||||
|
||||
if is_combinable:
|
||||
cost = cost - t0
|
||||
# combine sequenced pickup ρb and ps into ρu(union pickup)
|
||||
Pu, Pu_cycle = self.pickup_group_combine(Ps, Ps_cycle, Pd, Pd_cycle)
|
||||
|
||||
# decide the placement cluster and sequencing of pickup ρu
|
||||
pickup_action_counter, place_action_counter = 0, self.config.head_num - Pu.count(None)
|
||||
right_most_slot, left_most_slot = 0, self.config.slot_num // 2 # most left and right pickup slot
|
||||
|
||||
# === TODO: 机械限位、后槽位分配未处理 ===
|
||||
for head in range(self.config.head_num):
|
||||
if not Pu[head]:
|
||||
continue
|
||||
assert Pu[head] in self.feeder_part_arrange.keys()
|
||||
for slot in self.feeder_part_arrange[Pu[head]]:
|
||||
left_most_slot = min(slot - head * intv_ratio, left_most_slot)
|
||||
right_most_slot = max(slot - head * intv_ratio, right_most_slot)
|
||||
|
||||
# calculate forward, backward, pick and place traveling time
|
||||
t_FW, t_BW, t_PL, t_PU = 0, 0, 0, 0
|
||||
cycle = 0
|
||||
while cycle < max(Pu_cycle):
|
||||
mount_points = []
|
||||
for head, part in enumerate(Pu):
|
||||
if part is None or cycle > Pu_cycle[head]:
|
||||
continue
|
||||
idx = component_point_index[part]
|
||||
mount_points.append(Point(self.part_point_pos[part][idx].x - head * self.config.head_intv
|
||||
+ self.config.stopper_pos.x,
|
||||
self.part_point_pos[part][idx].y + self.config.stopper_pos.y))
|
||||
assert len(mount_points) > 0
|
||||
|
||||
# calculate cycle moving distance
|
||||
mount_points.sort(key=lambda p: p.x)
|
||||
slotf1_pos = self.config.slotf1_pos
|
||||
t_FW += max(
|
||||
abs(slotf1_pos.x + (left_most_slot - 1) * self.config.slot_intv - mount_points[0].x) / x_moving_speed,
|
||||
abs(slotf1_pos.y - mount_points[0].y) / y_moving_speed)
|
||||
t_BW += max(
|
||||
abs(slotf1_pos.x + (right_most_slot - 1) * self.config.slot_intv - mount_points[-1].x) / x_moving_speed,
|
||||
abs(slotf1_pos.y - mount_points[-1].y) / y_moving_speed)
|
||||
# pick up moving time
|
||||
t_PU += (right_most_slot - left_most_slot) * self.config.slot_intv / x_moving_speed
|
||||
# place moving time
|
||||
for idx_points in range(len(mount_points) - 1):
|
||||
t_PL += max(abs(mount_points[idx_points].x - mount_points[idx_points + 1].x) / x_moving_speed,
|
||||
abs(mount_points[idx_points].y - mount_points[idx_points + 1].y) / y_moving_speed)
|
||||
cycle += 1
|
||||
|
||||
t0 = t_FW + (t_PL + place_action_counter * place_time) + t_BW
|
||||
cost += (t_PU + pickup_action_counter * pick_time) + t0
|
||||
|
||||
if V[i - 1] + cost < V[j]:
|
||||
pickup_result[j], pickup_cycle_result[j] = Pu, Pu_cycle
|
||||
V_SNode[j] = i - 1
|
||||
V[j] = V[i - 1] + cost
|
||||
|
||||
Pd, Pd_cycle = Pu, Pu_cycle
|
||||
j += 1
|
||||
else:
|
||||
break
|
||||
|
||||
node = len(V) - 1
|
||||
while True:
|
||||
prev_node = V_SNode[node]
|
||||
if prev_node == -1:
|
||||
break
|
||||
for k in range(prev_node + 1, node):
|
||||
pickup_result[k], pickup_cycle_result[k] = [], []
|
||||
node = prev_node
|
||||
return V[-1], pickup_result, pickup_cycle_result
|
||||
|
||||
def convertor(self, individual):
|
||||
|
||||
part_result, cycle_result, feeder_slot_result = [], [], []
|
||||
# initial result
|
||||
_, pickup_result, pickup_cycle_result = self.cal_ind_val(individual)
|
||||
|
||||
for idx, pickup in enumerate(pickup_result):
|
||||
while pickup and max(pickup_cycle_result[idx]) != 0:
|
||||
cycle = min([cycle_ for cycle_ in pickup_cycle_result[idx] if cycle_ > 0])
|
||||
feeder_part_arrange_index = defaultdict(int)
|
||||
part_result.append([-1 for _ in range(self.config.head_num)])
|
||||
feeder_slot_result.append([-1 for _ in range(self.config.head_num)])
|
||||
cycle_result.append(cycle)
|
||||
for head, part in enumerate(pickup):
|
||||
if part is None or pickup_cycle_result[idx][head] == 0:
|
||||
continue
|
||||
|
||||
part_result[-1][head] = self.part_data[self.part_data['part'] == part].index.tolist()[0]
|
||||
feeder_slot_result[-1][head] = self.feeder_part_arrange[part][feeder_part_arrange_index[part]]
|
||||
feeder_part_arrange_index[part] += 1
|
||||
if feeder_part_arrange_index[part] >= len(self.feeder_part_arrange[part]):
|
||||
feeder_part_arrange_index[part] = 0
|
||||
|
||||
pickup_cycle_result[idx][head] -= cycle
|
||||
|
||||
return part_result, cycle_result, feeder_slot_result
|
||||
|
||||
def optimal_nozzle_assignment(self):
|
||||
if len(self.step_data) == 0:
|
||||
return defaultdict(int)
|
||||
|
||||
# === Nozzle Assignment ===
|
||||
# number of points for nozzle & number of heads for nozzle
|
||||
nozzle_points, nozzle_assigned_counter = defaultdict(int), defaultdict(int)
|
||||
|
||||
for _, data in self.step_data.iterrows():
|
||||
idx = self.part_data[self.part_data['part'] == data['part']].index.tolist()[0]
|
||||
nozzle = self.part_data.loc[idx]['nz']
|
||||
|
||||
nozzle_assigned_counter[nozzle] = 0
|
||||
nozzle_points[nozzle] += 1
|
||||
|
||||
assert len(nozzle_points.keys()) <= self.config.head_num
|
||||
total_points, available_head = len(self.step_data), self.config.head_num
|
||||
# S1: set of nozzle types which are sufficient to assign one nozzle to the heads
|
||||
# S2: temporary nozzle set
|
||||
# S3: set of nozzle types which already have the maximum reasonable nozzle amounts.
|
||||
S1, S2, S3 = [], [], []
|
||||
|
||||
for nozzle in nozzle_points.keys(): # Phase 1
|
||||
if nozzle_points[nozzle] * self.config.head_num < total_points:
|
||||
nozzle_assigned_counter[nozzle] = 1
|
||||
available_head -= 1
|
||||
total_points -= nozzle_points[nozzle]
|
||||
|
||||
S1.append(nozzle)
|
||||
else:
|
||||
S2.append(nozzle)
|
||||
|
||||
available_head_ = available_head # Phase 2
|
||||
for nozzle in S2:
|
||||
nozzle_assigned_counter[nozzle] = math.floor(available_head * nozzle_points[nozzle] / total_points)
|
||||
available_head_ = available_head_ - nozzle_assigned_counter[nozzle]
|
||||
|
||||
S2.sort(key=lambda x: nozzle_points[x] / (nozzle_assigned_counter[x] + 1e-10), reverse=True)
|
||||
while available_head_ > 0:
|
||||
nozzle = S2[0]
|
||||
nozzle_assigned_counter[nozzle] += 1
|
||||
|
||||
S2.remove(nozzle)
|
||||
S3.append(nozzle)
|
||||
available_head_ -= 1
|
||||
|
||||
phase_iteration = len(S2) - 1
|
||||
while phase_iteration > 0: # Phase 3
|
||||
nozzle_i_val, nozzle_j_val = 0, 0
|
||||
nozzle_i, nozzle_j = None, None
|
||||
for nozzle in S2:
|
||||
if nozzle_i is None or nozzle_points[nozzle] / nozzle_assigned_counter[nozzle] > nozzle_i_val:
|
||||
nozzle_i_val = nozzle_points[nozzle] / nozzle_assigned_counter[nozzle]
|
||||
nozzle_i = nozzle
|
||||
|
||||
if nozzle_assigned_counter[nozzle] > 1:
|
||||
if nozzle_j is None or nozzle_points[nozzle] / (nozzle_assigned_counter[nozzle] - 1) < nozzle_j_val:
|
||||
nozzle_j_val = nozzle_points[nozzle] / (nozzle_assigned_counter[nozzle] - 1)
|
||||
nozzle_j = nozzle
|
||||
|
||||
if nozzle_i and nozzle_j and nozzle_points[nozzle_j] / (nozzle_assigned_counter[nozzle_j] - 1) < \
|
||||
nozzle_points[nozzle_i] / nozzle_assigned_counter[nozzle_i]:
|
||||
nozzle_assigned_counter[nozzle_j] -= 1
|
||||
nozzle_assigned_counter[nozzle_i] += 1
|
||||
S2.remove(nozzle_i)
|
||||
S3.append(nozzle_i)
|
||||
else:
|
||||
break
|
||||
|
||||
return nozzle_assigned_counter
|
||||
|
||||
@timer_wrapper
|
||||
def optimize(self):
|
||||
nozzle_assigned_counter = self.optimal_nozzle_assignment()
|
||||
|
||||
# nozzle assignment result:
|
||||
self.designated_nozzle = [''] * self.config.head_num
|
||||
head_index = 0
|
||||
for nozzle, num in nozzle_assigned_counter.items():
|
||||
while num > 0:
|
||||
self.designated_nozzle[head_index] = nozzle
|
||||
head_index += 1
|
||||
num -= 1
|
||||
|
||||
# === component assignment ===
|
||||
part_points, nozzle_components = defaultdict(int), defaultdict(list) # 元件贴装点数,吸嘴-元件对应关系
|
||||
component_feeder_limit, component_divided_points = defaultdict(int), defaultdict(list)
|
||||
for _, data in self.step_data.iterrows():
|
||||
part = data['part']
|
||||
idx = self.part_data[self.part_data['part'] == part].index.tolist()[0]
|
||||
nozzle = self.part_data.loc[idx]['nz']
|
||||
|
||||
component_feeder_limit[part] = self.part_data.loc[idx].fdn
|
||||
part_points[part] += 1
|
||||
if nozzle_components[nozzle].count(part) < component_feeder_limit[part]:
|
||||
nozzle_components[nozzle].append(part)
|
||||
|
||||
for part, feeder_limit in component_feeder_limit.items():
|
||||
for _ in range(feeder_limit):
|
||||
component_divided_points[part].append(part_points[part] // feeder_limit)
|
||||
|
||||
for part, divided_points in component_divided_points.items():
|
||||
index = 0
|
||||
while sum(divided_points) < part_points[part]:
|
||||
divided_points[index] += 1
|
||||
index += 1
|
||||
|
||||
CT_Group, CT_Points = [], [] # CT: Component Type
|
||||
while sum(len(nozzle_components[nozzle]) for nozzle in nozzle_components.keys()) != 0:
|
||||
|
||||
CT_Group.append([None for _ in range(self.config.head_num)])
|
||||
CT_Points.append([0 for _ in range(self.config.head_num)])
|
||||
|
||||
for head_index in range(self.config.head_num):
|
||||
nozzle = self.designated_nozzle[head_index] # 分配的吸嘴
|
||||
if len(nozzle_components[nozzle]) == 0: # 无可用元件
|
||||
continue
|
||||
|
||||
max_points, designated_part = 0, None
|
||||
for part in nozzle_components[nozzle]:
|
||||
if part_points[part] > max_points:
|
||||
max_points = part_points[part]
|
||||
designated_part = part
|
||||
|
||||
part_points[designated_part] -= component_divided_points[designated_part][-1]
|
||||
|
||||
CT_Group[-1][head_index] = designated_part
|
||||
CT_Points[-1][head_index] = component_divided_points[designated_part][-1]
|
||||
|
||||
component_divided_points[designated_part].pop()
|
||||
nozzle_components[nozzle].remove(designated_part)
|
||||
|
||||
# === assign CT group to feeder slot ===
|
||||
for _, data in self.step_data.iterrows():
|
||||
self.part_point_pos[data.part].append(Point(data.x + self.config.stopper_pos.x,
|
||||
data.y + self.config.stopper_pos.y))
|
||||
|
||||
for pos_list in self.part_point_pos.values():
|
||||
pos_list.sort(key=lambda p: (p.x, p.y))
|
||||
|
||||
CT_Group_slot = [-1] * len(CT_Group)
|
||||
feeder_lane = [None] * self.config.slot_num # 供料器基座上已分配的元件类型
|
||||
CT_Head = defaultdict(list)
|
||||
for pickup in CT_Group:
|
||||
for head, CT in enumerate(pickup):
|
||||
if CT is None:
|
||||
continue
|
||||
if CT not in CT_Head:
|
||||
CT_Head[CT] = [head, head]
|
||||
CT_Head[CT][0] = min(CT_Head[CT][0], head)
|
||||
CT_Head[CT][1] = max(CT_Head[CT][1], head)
|
||||
|
||||
intv_ratio = self.config.head_intv // self.config.slot_intv
|
||||
for CTIdx, pickup in enumerate(CT_Group):
|
||||
best_slot = []
|
||||
for cp_index, part in enumerate(pickup):
|
||||
if part is None:
|
||||
continue
|
||||
best_slot.append(round((sum(p.x for p in self.part_point_pos[part]) / len(
|
||||
self.part_point_pos[part]) - self.config.slotf1_pos.x) / self.config.slot_intv) + 1 - cp_index * intv_ratio)
|
||||
best_slot = round(sum(best_slot) / len(best_slot))
|
||||
|
||||
search_dir, step = 0, 0 # dir: 1-向右, 0-向左
|
||||
prev_assign_available = True
|
||||
while True:
|
||||
assign_slot = best_slot + step if search_dir else best_slot - step
|
||||
if assign_slot + (len(pickup) - 1) * intv_ratio >= self.config.slot_num / 2 or assign_slot < 0:
|
||||
if not prev_assign_available:
|
||||
raise Exception('feeder assign error!')
|
||||
|
||||
# prev_assign_available = False
|
||||
search_dir = 1 - search_dir
|
||||
if search_dir == 1:
|
||||
step += 1
|
||||
continue
|
||||
|
||||
prev_assign_available = True
|
||||
assign_available = True
|
||||
|
||||
# 分配对应槽位
|
||||
for slot in range(assign_slot, assign_slot + intv_ratio * len(pickup), intv_ratio):
|
||||
pickup_index = int((slot - assign_slot) / intv_ratio)
|
||||
pick_part = pickup[pickup_index]
|
||||
|
||||
# 检查槽位占用情况
|
||||
if feeder_lane[slot] and pick_part:
|
||||
assign_available = False
|
||||
break
|
||||
|
||||
# 检查机械限位冲突
|
||||
if pick_part and (slot - CT_Head[pick_part][0] * intv_ratio <= 0 or slot + (
|
||||
self.config.head_num - CT_Head[pick_part][1] - 1) * intv_ratio > self.config.slot_num // 2):
|
||||
assign_available = False
|
||||
break
|
||||
|
||||
if assign_available:
|
||||
for idx, part in enumerate(pickup):
|
||||
if part:
|
||||
feeder_lane[assign_slot + idx * intv_ratio] = part
|
||||
CT_Group_slot[CTIdx] = assign_slot
|
||||
break
|
||||
|
||||
search_dir = 1 - search_dir
|
||||
if search_dir == 1:
|
||||
step += 1
|
||||
|
||||
# === Initial Pickup Group ===
|
||||
initial_pickup, initial_pickup_cycle = [], []
|
||||
for index, CT in enumerate(CT_Group):
|
||||
while True:
|
||||
if CT_Points[index].count(0) == self.config.head_num:
|
||||
break
|
||||
min_element = min([Points for Points in CT_Points[index] if Points > 0])
|
||||
|
||||
initial_pickup.append(copy.deepcopy(CT_Group[index]))
|
||||
initial_pickup_cycle.append(min_element)
|
||||
for head in range(self.config.head_num):
|
||||
if CT_Points[index][head] >= min_element:
|
||||
CT_Points[index][head] -= min_element
|
||||
if CT_Points[index][head] == 0:
|
||||
CT_Group[index][head] = None
|
||||
|
||||
# pickup partition rule
|
||||
partition_probability = 0.1
|
||||
for idx, Pickup in enumerate(initial_pickup):
|
||||
pickup_num = len([element for element in Pickup if element is not None])
|
||||
if 2 <= pickup_num <= self.config.head_num / 3 or (
|
||||
self.config.head_num / 3 <= pickup_num <= self.config.head_num / 2 and np.random.rand() < partition_probability):
|
||||
# partitioned into single component pickups
|
||||
# or partition the potentially inefficient initial pickups with a small probability
|
||||
pair_index = []
|
||||
for index, CT in enumerate(Pickup):
|
||||
if CT is not None:
|
||||
pair_index.append(len(self.pickup_group))
|
||||
self.pickup_group.append([None for _ in range(self.config.head_num)])
|
||||
self.pickup_group[-1][index] = CT
|
||||
self.pickup_group_cycle.append(initial_pickup_cycle[idx])
|
||||
self.pair_group.append(pair_index)
|
||||
else:
|
||||
self.pickup_group.append(Pickup)
|
||||
self.pickup_group_cycle.append(initial_pickup_cycle[idx])
|
||||
|
||||
# basic parameter
|
||||
# crossover rate & mutation rate: 80% & 10%
|
||||
# population size: 200
|
||||
# the number of generation: 500
|
||||
crossover_rate, mutation_rate = 0.8, 0.1
|
||||
population_size, n_generations = 200, 500
|
||||
|
||||
# initial solution
|
||||
population = []
|
||||
for _ in range(population_size):
|
||||
pop_permutation = list(range(len(self.pickup_group)))
|
||||
np.random.shuffle(pop_permutation)
|
||||
population.append(pop_permutation)
|
||||
|
||||
best_individual, best_pop_val = [], []
|
||||
|
||||
# === 记录不同元件对应的槽位 ===
|
||||
self.feeder_part_arrange = defaultdict(list)
|
||||
for slot in range(1, self.config.slot_num // 2 + 1):
|
||||
if feeder_lane[slot]:
|
||||
self.feeder_part_arrange[feeder_lane[slot]].append(slot)
|
||||
|
||||
# === 记录不同元件的注册吸嘴类型 ===
|
||||
self.part_nozzle = defaultdict(str)
|
||||
for pickup in self.pickup_group:
|
||||
for part in pickup:
|
||||
if part is None or part in self.part_nozzle.keys():
|
||||
continue
|
||||
self.part_nozzle[part] = self.part_data[self.part_data['part'] == part]['nz'].tolist()[0]
|
||||
|
||||
with tqdm(total=n_generations) as pbar:
|
||||
pbar.set_description('hybrid genetic process')
|
||||
# calculate fitness value
|
||||
pop_val = [self.cal_ind_val(individual)[0] for individual in population] # val is related to assembly time
|
||||
|
||||
for _ in range(n_generations):
|
||||
# min-max convert
|
||||
max_val = 1.5 * max(pop_val)
|
||||
convert_pop_val = list(map(lambda v: max_val - v, pop_val))
|
||||
|
||||
# crossover and mutation
|
||||
c = 0
|
||||
new_population, new_pop_val = [], []
|
||||
for pop in range(population_size):
|
||||
if pop % 2 == 0 and np.random.random() < crossover_rate:
|
||||
index1, index2 = GenOpe.roulette_wheel_selection(convert_pop_val), -1
|
||||
while True:
|
||||
index2 = GenOpe.roulette_wheel_selection(convert_pop_val)
|
||||
if index1 != index2:
|
||||
break
|
||||
# 两点交叉算子
|
||||
offspring1 = GenOpe.directed_edge_recombine_crossover(population[index1], population[index2])
|
||||
offspring2 = GenOpe.directed_edge_recombine_crossover(population[index2], population[index1])
|
||||
|
||||
if np.random.random() < mutation_rate:
|
||||
GenOpe.swap_mutation(offspring1)
|
||||
|
||||
if np.random.random() < mutation_rate:
|
||||
GenOpe.swap_mutation(offspring2)
|
||||
|
||||
new_population.append(offspring1)
|
||||
new_population.append(offspring2)
|
||||
|
||||
new_pop_val.append(self.cal_ind_val(offspring1)[0])
|
||||
new_pop_val.append(self.cal_ind_val(offspring2)[0])
|
||||
|
||||
# generate next generation
|
||||
top_k_index = GenOpe.get_top_kth(pop_val, population_size - len(new_population), reverse=False)
|
||||
for index in top_k_index:
|
||||
new_population.append(population[index])
|
||||
new_pop_val.append(pop_val[index])
|
||||
|
||||
population = new_population
|
||||
pop_val = new_pop_val
|
||||
pbar.update(1)
|
||||
|
||||
best_individual = population[np.argmin(pop_val)]
|
||||
self.result.part, self.result.cycle, self.result.slot = self.convertor(best_individual)
|
||||
self.result.point, self.result.sequence = self.path_planner.greedy_cluster(self.result.part, self.result.cycle,
|
||||
self.result.slot)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user