230 lines
11 KiB
Python
230 lines
11 KiB
Python
from base_optimizer.optimizer_common import *
|
|
|
|
|
|
def component_assign_evaluate(component_data, component_result, cycle_result, feeder_slot_result) -> float:
|
|
nozzle_change_counter = 0
|
|
for head in range(max_head_index):
|
|
nozzle = ''
|
|
for cycle in range(len(component_result)):
|
|
component_index = component_result[cycle][head]
|
|
if component_index == -1:
|
|
continue
|
|
|
|
if cycle != 0 and nozzle != component_data.loc[component_index, 'nz']:
|
|
nozzle_change_counter += 1
|
|
nozzle = component_data.loc[component_index, 'nz']
|
|
|
|
gang_pick_counter = 0
|
|
for cycle, feeder_slot in enumerate(feeder_slot_result):
|
|
pick_slot = defaultdict(int)
|
|
for head, slot in enumerate(feeder_slot):
|
|
if slot == -1:
|
|
continue
|
|
pick_slot[slot - head * interval_ratio] += 1
|
|
for _ in pick_slot.values():
|
|
gang_pick_counter += cycle_result[cycle]
|
|
|
|
return sum(cycle_result) + e_nz_change * nozzle_change_counter + e_gang_pick * gang_pick_counter
|
|
|
|
|
|
def convert_cell_2_result(pcb_data, component_data, component_cell, population):
|
|
assert component_cell['points'].sum() == len(pcb_data)
|
|
head_assignment = [[] for _ in range(max_head_index)]
|
|
|
|
wl = [0 for _ in range(max_head_index)] # workload
|
|
|
|
e1, e2, e3 = 1, 0.5, 1. / 6
|
|
|
|
component_result, cycle_result, feeder_slot_result = [], [], []
|
|
for index in population:
|
|
if component_cell.loc[index]['points'] == 0:
|
|
continue
|
|
# 元胞对应的元件类型和贴装点数
|
|
component_type, component_points = component_cell.loc[index, 'index'], component_cell.loc[index, 'points']
|
|
|
|
nozzle_change, maxwl = [0 for _ in range(max_head_index)], [0 for _ in range(max_head_index)]
|
|
for head in range(max_head_index):
|
|
if head_assignment[head]:
|
|
assigned_part = head_assignment[head][-1][0]
|
|
if component_data.loc[assigned_part]['nz'] != component_data.loc[component_type]['nz']:
|
|
nozzle_change[head] = 1
|
|
wl1 = wl.copy()
|
|
wl1[head] += component_points
|
|
maxwl[head] = max(wl1) + e1 * nozzle_change[head]
|
|
|
|
awl, wl2 = min(maxwl), wl.copy()
|
|
for idx, val in enumerate(maxwl):
|
|
if val > awl:
|
|
wl2[idx] += e3
|
|
head_ = wl2.index(min(wl2))
|
|
wl[head_] += component_points
|
|
head_assignment[head_].append([component_type, component_points])
|
|
|
|
head_assignment_counter = [0 for _ in range(max_head_index)]
|
|
while True:
|
|
assigned_part, assigned_cycle = [-1 for _ in range(max_head_index)], [0 for _ in range(max_head_index)]
|
|
for head in range(max_head_index):
|
|
counter = head_assignment_counter[head]
|
|
|
|
if head_assignment[head] and head_assignment[head][counter][1] > 0:
|
|
assigned_part[head] = head_assignment[head][counter][0]
|
|
assigned_cycle[head] = head_assignment[head][counter][1]
|
|
|
|
nonzero_cycle = [cycle for cycle in assigned_cycle if cycle > 0]
|
|
if not nonzero_cycle:
|
|
break
|
|
|
|
cycle = min(nonzero_cycle)
|
|
cycle_result.append(cycle)
|
|
component_result.append(assigned_part)
|
|
|
|
for head in range(max_head_index):
|
|
counter = head_assignment_counter[head]
|
|
|
|
if head_assignment[head] and head_assignment[head][counter][1] > 0:
|
|
head_assignment[head][counter][1] -= cycle_result[-1]
|
|
if head_assignment[head][counter][1] == 0 and counter < len(head_assignment[head]) - 1:
|
|
head_assignment_counter[head] += 1
|
|
|
|
feeder_slot_result = feeder_assignment(component_data, pcb_data, component_result, cycle_result)
|
|
return component_result, cycle_result, feeder_slot_result
|
|
|
|
|
|
@timer_wrapper
|
|
def optimizer_celldivision(pcb_data, component_data, hinter=True):
|
|
# Crossover method: Two-point crossover
|
|
# Mutation method: Swap
|
|
# Parent selection method: Roulette wheel
|
|
# Termination condition: 20 successive non-improvement iterations
|
|
population_size = 40 # 种群规模
|
|
crossover_rate, mutation_rate = .6, .02
|
|
golden_section = 0.618
|
|
|
|
# 获取元件元胞
|
|
point_num = len(pcb_data)
|
|
component_cell = pd.DataFrame({'index': np.arange(len(component_data)), 'points': np.zeros(len(component_data), dtype=int)})
|
|
for point_cnt in range(point_num):
|
|
part = pcb_data.loc[point_cnt, 'part']
|
|
index = np.where(component_data['part'].values == part)
|
|
component_cell.loc[index[0], 'points'] += 1
|
|
component_cell = component_cell[~component_cell['points'].isin([0])]
|
|
|
|
# component_cell.sort_values(by = "points" , inplace = True, ascending = False)
|
|
best_population, best_component_cell = [], []
|
|
min_pop_val = float('inf') # 最优种群价值
|
|
Div, Imp = 0, 0
|
|
while True:
|
|
# randomly generate permutations
|
|
generation_ = np.array(component_cell.index)
|
|
pop_generation = []
|
|
for _ in range(population_size):
|
|
np.random.shuffle(generation_)
|
|
pop_generation.append(generation_.tolist())
|
|
|
|
pop_val = []
|
|
for pop in range(population_size):
|
|
component_result, cycle_result, feeder_slot_result = convert_cell_2_result(pcb_data, component_data,
|
|
component_cell,
|
|
pop_generation[pop])
|
|
pop_val.append(
|
|
component_assign_evaluate(component_data, component_result, cycle_result, feeder_slot_result))
|
|
|
|
# 初始化随机生成种群
|
|
Upit = int(1.5 * np.sqrt(len(component_cell)))
|
|
|
|
while Div < Upit:
|
|
if hinter:
|
|
print('----- current div : ' + str(Div) + ' , total div : ' + str(Upit) + ' -----')
|
|
|
|
# 选择
|
|
new_pop_generation, new_pop_val = [], []
|
|
top_k_index = get_top_k_value(pop_val, int(population_size * 0.3))
|
|
for index in top_k_index:
|
|
new_pop_generation.append(pop_generation[index])
|
|
new_pop_val.append(pop_val[index])
|
|
index = [i for i in range(population_size)]
|
|
|
|
select_index = random.choices(index, weights=pop_val, k=population_size - int(population_size * 0.3))
|
|
for index in select_index:
|
|
new_pop_generation.append(pop_generation[index])
|
|
new_pop_val.append(pop_val[index])
|
|
pop_generation, pop_val = new_pop_generation, new_pop_val
|
|
|
|
# 交叉
|
|
for pop in range(population_size):
|
|
if pop % 2 == 0 and np.random.random() < crossover_rate:
|
|
index1, index2 = roulette_wheel_selection(pop_val), -1
|
|
while True:
|
|
index2 = roulette_wheel_selection(pop_val)
|
|
if index1 != index2:
|
|
break
|
|
# 两点交叉算子
|
|
pop_generation[index1], pop_generation[index2] = partially_mapped_crossover(pop_generation[index1],
|
|
pop_generation[index2])
|
|
|
|
if np.random.random() < mutation_rate:
|
|
index_ = roulette_wheel_selection(pop_val)
|
|
swap_mutation(pop_generation[index_])
|
|
|
|
# 将元件元胞分配到各个吸杆上,计算价值函数
|
|
for pop in range(population_size):
|
|
component_result, cycle_result, feeder_slot_result = convert_cell_2_result(pcb_data, component_data,
|
|
component_cell,
|
|
pop_generation[pop])
|
|
pop_val[pop] = component_assign_evaluate(component_data, component_result, cycle_result,
|
|
feeder_slot_result)
|
|
assert(pop_val[pop] > 0)
|
|
|
|
if min(pop_val) < min_pop_val:
|
|
min_pop_val = min(pop_val)
|
|
best_population = copy.deepcopy(pop_generation[np.argmin(pop_val)])
|
|
best_component_cell = copy.deepcopy(component_cell)
|
|
Div, Imp = 0, 1
|
|
else:
|
|
Div += 1
|
|
|
|
if Imp == 1:
|
|
Div, Imp = 0, 0
|
|
# Section: cell division operation
|
|
if hinter:
|
|
print(' ------------- cell division operation ------------- ')
|
|
division_component_cell = pd.DataFrame()
|
|
for idx, rows in component_cell.iterrows():
|
|
if component_cell.loc[idx, 'points'] <= 1:
|
|
division_component_cell = pd.concat([division_component_cell, pd.DataFrame([rows])],
|
|
ignore_index=True)
|
|
else:
|
|
division_component_cell = pd.concat([division_component_cell, pd.DataFrame([rows] * 2)],
|
|
ignore_index=True)
|
|
|
|
rows_counter = len(division_component_cell)
|
|
division_points = int(max(np.ceil(division_component_cell.loc[rows_counter - 2,
|
|
'points'] * golden_section), 1))
|
|
# 避免出现空元胞的情形
|
|
if division_points == 0 or division_points == division_component_cell.loc[
|
|
rows_counter - 2, 'points']:
|
|
division_component_cell.loc[rows_counter - 2, 'points'] = 1
|
|
else:
|
|
division_component_cell.loc[rows_counter - 2, 'points'] = division_points
|
|
|
|
division_component_cell.loc[rows_counter - 1, 'points'] -= division_component_cell.loc[
|
|
rows_counter - 2, 'points']
|
|
|
|
if division_component_cell.loc[rows_counter - 2, 'points'] == 0 or division_component_cell.loc[
|
|
rows_counter - 1, 'points'] == 0:
|
|
raise ValueError
|
|
|
|
component_cell = division_component_cell
|
|
|
|
# 完成分裂后重新生成染色体组
|
|
generation_ = np.array(range(len(component_cell)))
|
|
pop_generation = []
|
|
for _ in range(population_size):
|
|
np.random.shuffle(generation_)
|
|
pop_generation.append(generation_.tolist())
|
|
else:
|
|
break
|
|
|
|
assert(len(best_component_cell) == len(best_population))
|
|
return convert_cell_2_result(pcb_data, component_data, best_component_cell, best_population)
|