增加了HSMO整线优化方法,读取数据增加了供料器部分
This commit is contained in:
@@ -291,6 +291,328 @@ def evolutionary_component_assignment(pcb_data, component_data, machine_number,
|
||||
return min(pop_val), population[np.argmin(pop_val)]
|
||||
|
||||
|
||||
class SpiderMonkeyOpt:
|
||||
def __init__(self, pop_size, pcb_data, component_data, machine_number, estimator):
|
||||
self.PcbData = pcb_data
|
||||
self.ComponentData = component_data
|
||||
self.Estimator = estimator
|
||||
|
||||
self.PopSize = pop_size
|
||||
self.LocalLimit = pop_size
|
||||
self.GlobalLimit = pop_size
|
||||
|
||||
self.MachineNum = machine_number
|
||||
self.GroupSize = 0
|
||||
# self.Dim = sum(data.fdn for _, data in component_data.iterrows()) + machine_number
|
||||
|
||||
self.CpPoints = defaultdict(int)
|
||||
self.CpIndex = defaultdict(int)
|
||||
self.CpNozzle = defaultdict(str)
|
||||
|
||||
self.Dim = 0
|
||||
for cp_idx, data in component_data.iterrows():
|
||||
# cp_feeders[cp_idx] = data.fdn
|
||||
|
||||
division_data = copy.deepcopy(data)
|
||||
feeder_limit, total_points = division_data.fdn, division_data.points
|
||||
surplus_points = total_points % feeder_limit
|
||||
for _ in range(feeder_limit):
|
||||
division_data.fdn, division_data.points = 1, math.floor(total_points / feeder_limit)
|
||||
if surplus_points:
|
||||
division_data.points += 1
|
||||
surplus_points -= 1
|
||||
|
||||
self.CpPoints[self.Dim], self.CpNozzle[self.Dim] = division_data.points, division_data.nz
|
||||
self.CpIndex[self.Dim] = cp_idx
|
||||
self.Dim += 1
|
||||
|
||||
self.Dim += machine_number
|
||||
|
||||
component_list = list(range(len(self.CpPoints)))
|
||||
self.GenPosition = []
|
||||
self.GenPopVal = []
|
||||
for _ in range(self.PopSize):
|
||||
random.shuffle(component_list)
|
||||
self.GenPosition.append(component_list.copy())
|
||||
idx, prev = 0, 0
|
||||
div = random.sample(range(len(component_list)), self.MachineNum - 1)
|
||||
div.append(len(component_list))
|
||||
div.sort(reverse=False)
|
||||
|
||||
for _ in range(1, self.MachineNum + 1):
|
||||
self.GenPosition[-1].append(div[idx] - prev)
|
||||
prev = div[idx]
|
||||
idx += 1
|
||||
|
||||
self.GenPopVal.append(self.CalIndividualVal(self.GenPosition[-1]))
|
||||
|
||||
self.GroupPoint = [[0 for _ in range(2)] for _ in range(self.PopSize)]
|
||||
self.GroupPart = 1
|
||||
|
||||
self.GenProb = None
|
||||
self.GlobalMin = self.GenPopVal[0]
|
||||
self.GlobalLeaderPosition = self.GenPosition[0].copy()
|
||||
self.GlobalLimitCount = 0
|
||||
|
||||
self.LocalMin = np.ones(self.PopSize) * 1e10
|
||||
self.LocalLeaderPosition = [[0 for _ in range(self.Dim)] for _ in range(self.PopSize)]
|
||||
self.LocalLimitCount = [0 for _ in range(self.PopSize)]
|
||||
|
||||
for k in range(self.GroupSize):
|
||||
self.LocalMin[k] = self.GenPopVal[int(self.GroupPoint[k, 0])]
|
||||
self.LocalLeaderPosition[k,:] = self.GenPosition[int(self.GroupPoint[k, 0]),:]
|
||||
|
||||
self.CrossoverRatio = 0.1
|
||||
|
||||
|
||||
def GlobalLearning(self):
|
||||
GlobalTrial = self.GlobalMin
|
||||
for i in range(self.PopSize):
|
||||
if self.GenPopVal[i] < self.GlobalMin:
|
||||
self.GlobalMin = self.GenPopVal[i]
|
||||
self.GlobalLeaderPosition = self.GenPosition[i].copy()
|
||||
|
||||
if math.fabs(GlobalTrial - self.GlobalMin) < 1e-5:
|
||||
self.GlobalLimitCount = self.GlobalLimitCount + 1
|
||||
else:
|
||||
self.GlobalLimitCount = 0
|
||||
|
||||
def LocalLearning(self):
|
||||
OldMin = np.zeros(self.PopSize)
|
||||
for k in range(self.GroupSize):
|
||||
OldMin[k] = self.LocalMin[k]
|
||||
|
||||
for k in range(self.GroupSize):
|
||||
i = int(self.GroupPoint[k][0])
|
||||
while i <= int(self.GroupPoint[k][1]):
|
||||
if self.GenPopVal[i] < self.LocalMin[k]:
|
||||
self.LocalMin[k] = self.GenPopVal[i]
|
||||
self.LocalLeaderPosition[k] = self.GenPosition[i].copy()
|
||||
i = i + 1
|
||||
|
||||
for k in range(self.GroupSize):
|
||||
if math.fabs(OldMin[k] - self.LocalMin[k]) < 1e-5:
|
||||
self.LocalLimitCount[k] = self.LocalLimitCount[k] + 1
|
||||
else:
|
||||
self.LocalLimitCount[k] = 0
|
||||
|
||||
def CalculateProbabilities(self):
|
||||
self.GenProb = [0 for _ in range(self.PopSize)]
|
||||
MaxVal = self.GenPopVal[0]
|
||||
i = 1
|
||||
while i < self.PopSize:
|
||||
if self.GenPopVal[i] > MaxVal:
|
||||
MaxVal = self.GenPopVal[i]
|
||||
i += 1
|
||||
for i in range(self.PopSize):
|
||||
self.GenProb[i] = (0.9 * (self.GenPopVal[i] / MaxVal)) + 0.1
|
||||
|
||||
def LocalLeaderPhase(self, k):
|
||||
lo = int(self.GroupPoint[k][0])
|
||||
hi = int(self.GroupPoint[k][1])
|
||||
i = lo
|
||||
while i <= hi:
|
||||
|
||||
NewGene1, NewGene2 = self.CrossoverOperation(self.GenPosition[i], self.LocalLeaderPosition[k])
|
||||
NewGeneVal1, NewGeneVal2 = self.CalIndividualVal(NewGene1), self.CalIndividualVal(NewGene2)
|
||||
|
||||
if NewGeneVal1 < self.GenPopVal[i]:
|
||||
self.GenPosition[i] = NewGene1
|
||||
self.GenPopVal[i] = NewGeneVal1
|
||||
|
||||
if NewGeneVal2 < self.GenPopVal[i]:
|
||||
self.GenPosition[i] = NewGene2
|
||||
self.GenPopVal[i] = NewGeneVal2
|
||||
i += 1
|
||||
|
||||
def GlobalLeaderPhase(self, k):
|
||||
lo = int(self.GroupPoint[k][0])
|
||||
hi = int(self.GroupPoint[k][1])
|
||||
i = lo
|
||||
l = lo
|
||||
while l < hi:
|
||||
if random.random() < self.GenProb[i]:
|
||||
l += 1
|
||||
NewGene1, NewGene2 = self.CrossoverOperation(self.GenPosition[i], self.GlobalLeaderPosition)
|
||||
NewGeneVal1, NewGeneVal2 = self.CalIndividualVal(NewGene1), self.CalIndividualVal(NewGene2)
|
||||
|
||||
if NewGeneVal1 < self.GenPopVal[i]:
|
||||
self.GenPosition[i] = NewGene1
|
||||
self.GenPopVal[i] = NewGeneVal1
|
||||
|
||||
if NewGeneVal2 < self.GenPopVal[i]:
|
||||
self.GenPosition[i] = NewGene2
|
||||
self.GenPopVal[i] = NewGeneVal2
|
||||
|
||||
i += 1
|
||||
if i == hi:
|
||||
i = lo
|
||||
|
||||
def LocalLeaderDecision(self):
|
||||
for k in range(self.GroupSize):
|
||||
if self.LocalLimitCount[k] > self.LocalLimit:
|
||||
i = self.GroupPoint[k][0]
|
||||
while i <= int(self.GroupPoint[k][1]):
|
||||
if random.random() >= self.CrossoverRatio:
|
||||
NewGenPosition = list(range(self.Dim - self.MachineNum))
|
||||
random.shuffle(NewGenPosition)
|
||||
|
||||
idx, prev = 0, 0
|
||||
div = random.sample(range(len(NewGenPosition)), self.MachineNum - 1)
|
||||
div.append(len(NewGenPosition))
|
||||
div.sort(reverse=False)
|
||||
|
||||
for _ in range(1, self.MachineNum + 1):
|
||||
NewGenPosition.append(div[idx] - prev)
|
||||
prev = div[idx]
|
||||
idx += 1
|
||||
|
||||
NewGenVal = self.CalIndividualVal(NewGenPosition)
|
||||
|
||||
if NewGenVal < self.GenPopVal[i]:
|
||||
self.GenPosition[i] = NewGenPosition.copy()
|
||||
self.GenPopVal[i] = NewGenVal
|
||||
else:
|
||||
NewGene1, NewGene2 = self.CrossoverOperation(self.GenPosition[i], self.GlobalLeaderPosition)
|
||||
NewGeneVal1, NewGeneVal2 = self.CalIndividualVal(NewGene1), self.CalIndividualVal(NewGene2)
|
||||
|
||||
if NewGeneVal1 < self.GenPopVal[i]:
|
||||
self.GenPosition[i] = NewGene1.copy()
|
||||
self.GenPopVal[i] = NewGeneVal1
|
||||
|
||||
if NewGeneVal2 < self.GenPopVal[i]:
|
||||
self.GenPosition[i] = NewGene2.copy()
|
||||
self.GenPopVal[i] = NewGeneVal2
|
||||
|
||||
i += 1
|
||||
|
||||
self.LocalLimitCount[k] = 0
|
||||
|
||||
def GlobalLeaderDecision(self):
|
||||
if self.GlobalLimitCount> self.GlobalLimit:
|
||||
self.GroupPart += 1
|
||||
self.GlobalLimitCount = 0
|
||||
self.CreateGroup()
|
||||
self.LocalLearning()
|
||||
|
||||
def CreateGroup(self):
|
||||
g = 0
|
||||
lo = 0
|
||||
|
||||
while lo < self.PopSize:
|
||||
hi = lo + int(self.PopSize / self.GroupPart)
|
||||
self.GroupPoint[g][0] = lo
|
||||
self.GroupPoint[g][1] = hi
|
||||
if self.PopSize - hi < int(self.PopSize / self.GroupPart):
|
||||
self.GroupPoint[g][1] = (self.PopSize - 1)
|
||||
g = g + 1
|
||||
lo = hi + 1
|
||||
self.GroupSize = g
|
||||
|
||||
def CrossoverOperation(self, gene1, gene2):
|
||||
len_ = len(gene1)
|
||||
sub1, sub2 = partially_mapped_crossover(gene1[0: len_ - self.MachineNum], gene2[0: len_ - self.MachineNum])
|
||||
|
||||
pos1, pos2 = random.randint(0, self.MachineNum - 1), random.randint(0, self.MachineNum - 1)
|
||||
machine_assign1, machine_assign2 = gene1[len_ - self.MachineNum:], gene2[len_ - self.MachineNum:]
|
||||
machine_assign1[pos1], machine_assign2[pos2] = machine_assign2[pos2], machine_assign1[pos1]
|
||||
while sum(machine_assign1) != len_ - self.MachineNum:
|
||||
machine_idx = random.randint(0, self.MachineNum - 1)
|
||||
if machine_assign1[machine_idx] == 0:
|
||||
continue
|
||||
if sum(machine_assign1) > len_ - self.MachineNum:
|
||||
machine_assign1[machine_idx] -= 1
|
||||
else:
|
||||
machine_assign1[machine_idx] += 1
|
||||
|
||||
while sum(machine_assign2) != len_ - self.MachineNum:
|
||||
machine_idx = random.randint(0, self.MachineNum - 1)
|
||||
if machine_assign2[machine_idx] == 0:
|
||||
continue
|
||||
if sum(machine_assign2) > len_ - self.MachineNum:
|
||||
machine_assign2[machine_idx] -= 1
|
||||
else:
|
||||
machine_assign2[machine_idx] += 1
|
||||
|
||||
sub1.extend(machine_assign1)
|
||||
sub2.extend(machine_assign2)
|
||||
return sub1, sub2
|
||||
|
||||
|
||||
def CalIndividualVal(self, gene):
|
||||
ComponentNum = len(self.ComponentData)
|
||||
assignment_result = [[0 for _ in range(ComponentNum)] for _ in range(self.MachineNum)]
|
||||
|
||||
idx, machine_index = 0, 0
|
||||
for num in range(self.Dim - self.MachineNum, self.Dim):
|
||||
for _ in range(gene[num]):
|
||||
assignment_result[machine_index][self.CpIndex[gene[idx]]] += self.CpPoints[gene[idx]]
|
||||
idx += 1
|
||||
machine_index += 1
|
||||
|
||||
val = 0
|
||||
cp_items = converter(self.PcbData, self.ComponentData, assignment_result)
|
||||
for machine_index in range(self.MachineNum):
|
||||
cp_points, cp_nozzle, board_width, board_height = cp_items[machine_index]
|
||||
val = max(val, self.Estimator.predict(cp_points, cp_nozzle, board_width, board_height))
|
||||
return val
|
||||
|
||||
def spider_monkey_component_assignment(pcb_data, component_data, machine_number, estimator):
|
||||
population_size, iteration_number = 20, 50
|
||||
|
||||
smo = SpiderMonkeyOpt(population_size, pcb_data, component_data, machine_number, estimator)
|
||||
|
||||
# ========================== Calling: GlobalLearning() ======================== #
|
||||
smo.GlobalLearning()
|
||||
|
||||
# ========================== Calling: create_group() ========================== #
|
||||
smo.CreateGroup()
|
||||
|
||||
# ========================= Calling: LocalLearning() ========================== #
|
||||
smo.LocalLearning()
|
||||
|
||||
# ================================= Looping ================================== #
|
||||
with tqdm(total=iteration_number) as pbar:
|
||||
pbar.set_description('spider monkey algorithm process for PCB assembly line balance')
|
||||
for _ in range(iteration_number):
|
||||
for k in range(smo.GroupSize):
|
||||
# ==================== Calling: LocalLeaderPhase() =================== #
|
||||
smo.LocalLeaderPhase(k)
|
||||
|
||||
# =================== Calling: CalculateProbabilities() ================== #
|
||||
smo.CalculateProbabilities()
|
||||
|
||||
for k in range(smo.GroupSize):
|
||||
# ==================== Calling: GlobalLeaderPhase() ================== #
|
||||
smo.GlobalLeaderPhase(k)
|
||||
|
||||
# ======================= Calling: GlobalLearning() ====================== #
|
||||
smo.GlobalLearning()
|
||||
|
||||
# ======================= Calling: LocalLearning() ======================= #
|
||||
smo.LocalLearning()
|
||||
|
||||
# ================== Calling: LocalLeaderDecision() ====================== #
|
||||
smo.LocalLeaderDecision()
|
||||
|
||||
# ===================== Calling: GlobalLeaderDecision() ================== #
|
||||
smo.GlobalLeaderDecision()
|
||||
|
||||
pbar.update(1)
|
||||
|
||||
assignment_result = [[0 for _ in range(len(component_data))] for _ in range(machine_number)]
|
||||
|
||||
idx, machine_index = 0, 0
|
||||
for num in range(smo.Dim - machine_number, smo.Dim):
|
||||
for _ in range(smo.GlobalLeaderPosition[num]):
|
||||
assignment_result[machine_index][smo.CpIndex[smo.GlobalLeaderPosition[idx]]] += \
|
||||
smo.CpPoints[smo.GlobalLeaderPosition[idx]]
|
||||
idx += 1
|
||||
machine_index += 1
|
||||
|
||||
return smo.GlobalMin, assignment_result
|
||||
|
||||
|
||||
@timer_wrapper
|
||||
def line_optimizer_reconfiguration(component_data, pcb_data, machine_number):
|
||||
# === assignment of heads to modules is omitted ===
|
||||
@@ -303,9 +625,8 @@ def line_optimizer_reconfiguration(component_data, pcb_data, machine_number):
|
||||
print('random component allocation algorithm process for PCB assembly line balance')
|
||||
val, assignment = random_component_assignment(pcb_data, component_data, machine_number, estimator)
|
||||
elif i == 1:
|
||||
# brute force
|
||||
# which is proved to be useless, since it only ran in reasonable time for the smaller test instances
|
||||
continue
|
||||
# spider monkey
|
||||
val, assignment = spider_monkey_component_assignment(pcb_data, component_data, machine_number, estimator)
|
||||
elif i == 2:
|
||||
# local search
|
||||
print('local search component allocation algorithm process for PCB assembly line balance')
|
||||
@@ -314,7 +635,8 @@ def line_optimizer_reconfiguration(component_data, pcb_data, machine_number):
|
||||
# evolutionary
|
||||
val, assignment = evolutionary_component_assignment(pcb_data, component_data, machine_number, estimator)
|
||||
else:
|
||||
# greedy: unclear description
|
||||
# brute force
|
||||
# which is proved to be useless, since it only ran in reasonable time for the smaller test instances
|
||||
continue
|
||||
|
||||
if optimal_val is None or val < optimal_val:
|
||||
@@ -324,3 +646,5 @@ def line_optimizer_reconfiguration(component_data, pcb_data, machine_number):
|
||||
raise Exception('no feasible solution! ')
|
||||
|
||||
return optimal_assignment
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user