调整工程架构,增补了几种算法,初步添加神经网路训练拟合代码
This commit is contained in:
@ -1,12 +1,9 @@
|
||||
# implementation of <<An integrated allocation method for the PCB assembly line balancing problem with nozzle changes>>
|
||||
import copy
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from base_optimizer.optimizer_common import *
|
||||
from optimizer_hyperheuristic import *
|
||||
|
||||
|
||||
def selective_initialization(component_points, component_feeders, population_size):
|
||||
def selective_initialization(component_points, component_feeders, population_size, machine_number):
|
||||
population = [] # population initialization
|
||||
for _ in range(population_size):
|
||||
individual = []
|
||||
@ -14,14 +11,14 @@ def selective_initialization(component_points, component_feeders, population_siz
|
||||
if points == 0:
|
||||
continue
|
||||
# 可用机器数
|
||||
avl_machine_num = random.randint(1, min(max_machine_index, component_feeders[part_index], points))
|
||||
avl_machine_num = random.randint(1, min(machine_number, component_feeders[part_index], points))
|
||||
|
||||
selective_possibility = []
|
||||
for p in range(1, avl_machine_num + 1):
|
||||
selective_possibility.append(pow(2, avl_machine_num - p + 1))
|
||||
|
||||
sel_machine_num = random_selective([p + 1 for p in range(avl_machine_num)], selective_possibility) # 选择的机器数
|
||||
sel_machine_set = random.sample([p for p in range(max_machine_index)], sel_machine_num)
|
||||
sel_machine_set = random.sample([p for p in range(machine_number)], sel_machine_num)
|
||||
|
||||
sel_machine_points = [1 for _ in range(sel_machine_num)]
|
||||
for p in range(sel_machine_num - 1):
|
||||
@ -34,7 +31,7 @@ def selective_initialization(component_points, component_feeders, population_siz
|
||||
sel_machine_points[-1] += (points - sum(sel_machine_points))
|
||||
|
||||
# code component allocation into chromosome
|
||||
for p in range(max_machine_index):
|
||||
for p in range(machine_number):
|
||||
if p in sel_machine_set:
|
||||
individual += [0 for _ in range(sel_machine_points[0])]
|
||||
sel_machine_points.pop(0)
|
||||
@ -45,7 +42,7 @@ def selective_initialization(component_points, component_feeders, population_siz
|
||||
return population
|
||||
|
||||
|
||||
def selective_crossover(component_points, component_feeders, mother, father, non_decelerating=True):
|
||||
def selective_crossover(component_points, component_feeders, mother, father, machine_number, non_decelerating=True):
|
||||
assert len(mother) == len(father)
|
||||
|
||||
offspring1, offspring2 = mother.copy(), father.copy()
|
||||
@ -56,24 +53,24 @@ def selective_crossover(component_points, component_feeders, mother, father, non
|
||||
one_counter = 0
|
||||
|
||||
idx_, mother_cut_line, father_cut_line = 0, [-1], [-1]
|
||||
for idx_, gene in enumerate(mother[idx: idx + points + max_machine_index - 1]):
|
||||
for idx_, gene in enumerate(mother[idx: idx + points + machine_number - 1]):
|
||||
if gene:
|
||||
mother_cut_line.append(idx_)
|
||||
mother_cut_line.append(idx_ + 1)
|
||||
|
||||
for idx_, gene in enumerate(father[idx: idx + points + max_machine_index - 1]):
|
||||
for idx_, gene in enumerate(father[idx: idx + points + machine_number - 1]):
|
||||
if gene:
|
||||
father_cut_line.append(idx_)
|
||||
father_cut_line.append(idx_ + 1)
|
||||
|
||||
for offset in range(points + max_machine_index - 1):
|
||||
for offset in range(points + machine_number - 1):
|
||||
if mother[idx + offset] == 1:
|
||||
one_counter += 1
|
||||
if father[idx + offset] == 1:
|
||||
one_counter -= 1
|
||||
|
||||
# first constraint: the total number of '1's (the number of partitions) in the chromosome is unchanged
|
||||
if one_counter != 0 or offset == 0 or offset == points + max_machine_index - 2:
|
||||
if one_counter != 0 or offset == 0 or offset == points + machine_number - 2:
|
||||
continue
|
||||
|
||||
# the selected cut-line should guarantee there are the same or a larger number unassigned machine
|
||||
@ -89,13 +86,14 @@ def selective_crossover(component_points, component_feeders, mother, father, non
|
||||
n_new += 1
|
||||
|
||||
# second constraint: non_decelerating or accelerating crossover
|
||||
# non_decelerating or accelerating means that the number of machine without workload is increased
|
||||
if n_new < n_bro or (n_new == n_bro and not non_decelerating):
|
||||
continue
|
||||
|
||||
# third constraint (customized constraint):
|
||||
# no more than the maximum number of available machine for each component type
|
||||
new_mother_cut_line, new_father_cut_line = [], []
|
||||
for idx_ in range(max_machine_index + 1):
|
||||
for idx_ in range(machine_number + 1):
|
||||
if mother_cut_line[idx_] <= offset:
|
||||
new_mother_cut_line.append(mother_cut_line[idx_])
|
||||
else:
|
||||
@ -110,11 +108,11 @@ def selective_crossover(component_points, component_feeders, mother, father, non
|
||||
sorted(new_father_cut_line, reverse=False)
|
||||
n_mother_machine, n_father_machine = 0, 0
|
||||
|
||||
for idx_ in range(max_machine_index):
|
||||
if new_mother_cut_line[idx_ + 1] - new_mother_cut_line[idx_]:
|
||||
for idx_ in range(machine_number):
|
||||
if new_mother_cut_line[idx_ + 1] - new_mother_cut_line[idx_] > 1:
|
||||
n_mother_machine += 1
|
||||
|
||||
if new_father_cut_line[idx_ + 1] - new_father_cut_line[idx_]:
|
||||
if new_father_cut_line[idx_ + 1] - new_father_cut_line[idx_] > 1:
|
||||
n_father_machine += 1
|
||||
|
||||
if n_mother_machine > component_feeders[part_index] or n_father_machine > component_feeders[part_index]:
|
||||
@ -122,7 +120,7 @@ def selective_crossover(component_points, component_feeders, mother, father, non
|
||||
|
||||
feasible_cut_line.append(idx + offset)
|
||||
|
||||
idx += (points + max_machine_index - 1)
|
||||
idx += (points + machine_number - 1)
|
||||
|
||||
if len(feasible_cut_line) == 0:
|
||||
return offspring1, offspring2
|
||||
@ -133,14 +131,14 @@ def selective_crossover(component_points, component_feeders, mother, father, non
|
||||
return offspring1, offspring2
|
||||
|
||||
|
||||
def cal_individual_val(component_points, component_nozzle, individual):
|
||||
def cal_individual_val(component_points, component_feeders, component_nozzle, machine_number, individual, data_mgr, net):
|
||||
idx, objective_val = 0, []
|
||||
machine_component_points = [[] for _ in range(max_machine_index)]
|
||||
machine_component_points = [[] for _ in range(machine_number)]
|
||||
nozzle_component_points = defaultdict(list)
|
||||
|
||||
# decode the component allocation
|
||||
for comp_idx, points in component_points:
|
||||
component_gene = individual[idx: idx + points + max_machine_index - 1]
|
||||
component_gene = individual[idx: idx + points + machine_number - 1]
|
||||
machine_idx, component_counter = 0, 0
|
||||
for gene in component_gene:
|
||||
if gene:
|
||||
@ -150,14 +148,31 @@ def cal_individual_val(component_points, component_nozzle, individual):
|
||||
else:
|
||||
component_counter += 1
|
||||
machine_component_points[-1].append(component_counter)
|
||||
idx += (points + max_machine_index - 1)
|
||||
idx += (points + machine_number - 1)
|
||||
|
||||
nozzle_component_points[component_nozzle[comp_idx]] = [0] * len(component_points) # 初始化元件-吸嘴点数列表
|
||||
|
||||
# ======== 新加的开始 ========
|
||||
for machine_idx in range(machine_number):
|
||||
cp_points, cp_nozzle = defaultdict(int), defaultdict(str)
|
||||
for comp_idx, _ in component_points:
|
||||
if machine_component_points[machine_idx][comp_idx] == 0:
|
||||
continue
|
||||
cp_points['C' + str(comp_idx)] = machine_component_points[machine_idx][comp_idx]
|
||||
cp_nozzle['C' + str(comp_idx)] = component_nozzle[comp_idx]
|
||||
|
||||
encoding = np.array(data_mgr.encode(cp_points, cp_nozzle, 45, 150))
|
||||
encoding = torch.from_numpy(encoding.reshape((-1, np.shape(encoding)[0]))).float().to("cuda")
|
||||
# pred_time = net(encoding)[0, 0].item()
|
||||
# objective_val.append(pred_time * sum(points for points in cp_points.values()))
|
||||
objective_val.append(net(encoding)[0, 0].item())
|
||||
|
||||
return objective_val, machine_component_points
|
||||
# ======== 新加的结束(以下内容弃用) =====
|
||||
for comp_idx, points in component_points:
|
||||
nozzle_component_points[component_nozzle[comp_idx]][comp_idx] = points
|
||||
|
||||
for machine_idx in range(max_machine_index):
|
||||
for machine_idx in range(machine_number):
|
||||
nozzle_points = defaultdict(int)
|
||||
for idx, nozzle in component_nozzle.items():
|
||||
if component_points[idx] == 0:
|
||||
@ -236,20 +251,39 @@ def cal_individual_val(component_points, component_nozzle, individual):
|
||||
for idx in range(len(heads_placement) // max_head_index):
|
||||
wl += heads_placement[idx][1]
|
||||
objective_val.append(T_pp * machine_points + T_tr * wl + T_nc * ul + T_pl * pl)
|
||||
<<<<<<< HEAD
|
||||
|
||||
=======
|
||||
>>>>>>> 87ddb057cadf152d7af793aa7b8da439dedbe361
|
||||
return objective_val, machine_component_points
|
||||
|
||||
|
||||
def assemblyline_optimizer_genetic(pcb_data, component_data):
|
||||
def individual_convert(component_points, individual):
|
||||
machine_number = len(individual)
|
||||
machine_component_points = [[] for _ in range(machine_number)]
|
||||
idx = 0
|
||||
# decode the component allocation
|
||||
for comp_idx, points in component_points:
|
||||
component_gene = individual[idx: idx + points + machine_number - 1]
|
||||
machine_idx, component_counter = 0, 0
|
||||
for gene in component_gene:
|
||||
if gene:
|
||||
machine_component_points[machine_idx].append(component_counter)
|
||||
machine_idx += 1
|
||||
component_counter = 0
|
||||
else:
|
||||
component_counter += 1
|
||||
machine_component_points[-1].append(component_counter)
|
||||
idx += (points + machine_number - 1)
|
||||
|
||||
return machine_component_points
|
||||
|
||||
|
||||
def assemblyline_optimizer_genetic(pcb_data, component_data, machine_number):
|
||||
# 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
|
||||
# population_size, n_generations = 30, 50
|
||||
|
||||
# the number of placement points, the number of available feeders, and nozzle type of component respectively
|
||||
component_points, component_feeders, component_nozzle = defaultdict(int), defaultdict(int), defaultdict(str)
|
||||
@ -262,9 +296,16 @@ def assemblyline_optimizer_genetic(pcb_data, component_data):
|
||||
component_nozzle[part_index] = nozzle
|
||||
|
||||
component_points = sorted(component_points.items(), key=lambda x: x[0]) # 决定染色体排列顺序
|
||||
data_mgr = DataMgr()
|
||||
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
||||
net = Net(input_size=data_mgr.get_feature(), output_size=1).to(device)
|
||||
|
||||
net.load_state_dict(torch.load('model_state.pth'))
|
||||
# optimizer = torch.optim.Adam(net.parameters(), lr=0.1)
|
||||
# optimizer.load_state_dict(torch.load('optimizer_state.pth'))
|
||||
|
||||
# population initialization
|
||||
population = selective_initialization(component_points, component_feeders, population_size)
|
||||
population = selective_initialization(component_points, component_feeders, population_size, machine_number)
|
||||
with tqdm(total=n_generations) as pbar:
|
||||
pbar.set_description('genetic algorithm process for PCB assembly line balance')
|
||||
|
||||
@ -273,7 +314,8 @@ def assemblyline_optimizer_genetic(pcb_data, component_data):
|
||||
# calculate fitness value
|
||||
pop_val = []
|
||||
for individual in population:
|
||||
val, assigned_points = cal_individual_val(component_points, component_nozzle, individual)
|
||||
val, assigned_points = cal_individual_val(component_points, component_feeders, component_nozzle,
|
||||
machine_number, individual, data_mgr, net)
|
||||
pop_val.append(max(val))
|
||||
|
||||
select_index = get_top_k_value(pop_val, population_size - len(new_population), reverse=False)
|
||||
@ -282,7 +324,8 @@ def assemblyline_optimizer_genetic(pcb_data, component_data):
|
||||
|
||||
population += new_population
|
||||
for individual in new_population:
|
||||
val, _ = cal_individual_val(component_points, component_nozzle, individual)
|
||||
val, _ = cal_individual_val(component_points, component_feeders, component_nozzle, machine_number,
|
||||
individual, data_mgr, net)
|
||||
pop_val.append(max(val))
|
||||
|
||||
# min-max convert
|
||||
@ -302,13 +345,13 @@ def assemblyline_optimizer_genetic(pcb_data, component_data):
|
||||
break
|
||||
|
||||
offspring1, offspring2 = selective_crossover(component_points, component_feeders,
|
||||
population[index1], population[index2])
|
||||
population[index1], population[index2], machine_number)
|
||||
|
||||
if np.random.random() < mutation_rate:
|
||||
offspring1 = constraint_swap_mutation(component_points, offspring1)
|
||||
offspring1 = constraint_swap_mutation(component_points, offspring1, machine_number)
|
||||
|
||||
if np.random.random() < mutation_rate:
|
||||
offspring2 = constraint_swap_mutation(component_points, offspring2)
|
||||
offspring2 = constraint_swap_mutation(component_points, offspring2, machine_number)
|
||||
|
||||
new_population.append(offspring1)
|
||||
new_population.append(offspring2)
|
||||
@ -316,12 +359,13 @@ def assemblyline_optimizer_genetic(pcb_data, component_data):
|
||||
pbar.update(1)
|
||||
|
||||
best_individual = population[np.argmax(pop_val)]
|
||||
_, assignment_result = cal_individual_val(component_points, component_nozzle, best_individual)
|
||||
|
||||
val, assignment_result = cal_individual_val(component_points, component_feeders, component_nozzle, machine_number,
|
||||
best_individual, data_mgr, net)
|
||||
print('final value: ', val)
|
||||
# available feeder check
|
||||
for part_index, data in component_data.iterrows():
|
||||
feeder_limit = data['feeder-limit']
|
||||
for machine_index in range(max_machine_index):
|
||||
for machine_index in range(machine_number):
|
||||
if assignment_result[machine_index][part_index]:
|
||||
feeder_limit -= 1
|
||||
assert feeder_limit >= 0
|
||||
|
Reference in New Issue
Block a user