Article directory
1. Fuzzy job shop
1.1 Fuzzy numbers
The fuzzy set A on the domain of discourse X is represented by the membership function u(x), and u(x) takes the value [0,1]. If A is a triangular fuzzy number, A can be expressed as (a1, aM, a2). If A is a four-corner fuzzy number, A can be expressed as (a1, a2, a4, a5). Each fuzzy number and membership function are as follows:
For the workpiece processing time and completion time under uncertain conditions, triangular fuzzy numbers are used to describe. For the delivery date under uncertain conditions, trapezoidal fuzzy numbers are used to describe it. For example, for the first operation O11 of job1, its fuzzy processing time on machine 1 is represented by triangular fuzzy numbers (4, 7, 12), and the three numbers represent the best, most likely, and worst processing time respectively. A data case of flexible job shop scheduling problem with 4job and 2machine with fuzzy processing time is as follows: the parentheses are the actual processing time, which can be ignored
1.2 Triangular fuzzy number operation
【1】Sakawa, M., & Mori, T. (1999). An efficient genetic algorithm for job-shop scheduling problems with fuzzy processing time and fuzzy duedate. Computers & Industrial Engineering, 36(2), 325-341. doi:https://doi.org/10.1016/S0360-8352(99)00135-7
【2】Sakawa, M., & Kubota, R. (2000). Fuzzy programming for multiobjective job shop scheduling with fuzzy processing time and fuzzy duedate through genetic algorithms. European Journal of Operational Research, 120(2), 393-407. doi:https://doi.org/10.1016/S0377-2217(99)00094-6
To solve the workshop scheduling problem, you will encounter some calculations of processing time, such as calculating the processing start time and processing completion time. Like definite number operations, fuzzy number operations are also very important, mainly including fuzzy number summation and large operations and a comparison of fuzzy numbers.
- Summation: calculates the fuzzy completion time of each operation [1]
- Take the big: Calculate the fuzzy start time of each operation [1]
- Comparison: The fuzzy completion time is the sum of the fuzzy processing time, so it becomes a triangular fuzzy number. If you want to minimize the maximum fuzzy completion time, you need to compare some fuzzy completion time values [2]
- example
Suppose there are 2✖️2 JSP questions:
Job 1: Machine 1 (2, 5, 6), Machine 2 (5, 7, 8)
Job 2: Machine 2 (3, 4, 7), Machine 1 (1, 2 , 3)
Then the fuzzy start time of the second operation of Job1 is (2, 5, 6)V(3, 4, 7), that is (3, 5, 7); the fuzzy completion time is (3, 5, 7) +(5, 7, 8)=(8, 12, 15)
Suppose there are 4 triangular fuzzy numbers A1=(2,5,8), A2=(3,4,9), A3=(3,5 ,7),A4=(4,5,8), A4,A1,A3,A2 can be obtained according to the comparison principle
1.3 Fuzzy Gantt chart
Since the start time and completion time of each operation are triangular fuzzy numbers, its Gantt chart is also different from the normal Gantt chart, as shown in the figure below, the fuzzy start time of each operation is below the line, and the fuzzy completion time is above the line. Each triangle is the image of the aforementioned membership function. If it is the first processing on the machine, it is represented by a rectangle. For example, the fuzzy start time of O41 is (4,5,7), and the next step is to process on M1, and the fastest fuzzy end time of M1 is (6,10,14) of O31, so the fuzzy start time of O42 is (6, 10,14).
2. FJSP+fuzzy processing time+GA
2.1 GA algorithm settings
复现文章:【3】Lei, D. M. (2010). A genetic algorithm for flexible job shop scheduling with fuzzy processing time. International Journal of Production Research, 48(10), 2995-3013. doi:10.1080/00207540902814348
Different from the previous introduction, [3] proposed a new max operation operator, which is determined directly according to the rank result, and the larger value of the two triangular fuzzy numbers s, t is either s or t
- encoding and decoding (two-string)
It is represented by two layers of coding, one layer is the operation sequence, and the other layer is the selection machine. Note that the machine allocation code of the second layer corresponds to the machine that each operation is arranged in order, and has nothing to do with the first layer of coding. For example, operation 011 is the final processing, and the processing machine is 1.
In the two-level encoding in some other literatures, the second-level machine encoding corresponds to the processing machine for each operation under the first-level encoding, which is different from here. The following is another encoding method, such as operation 023 final processing, and the processing machine is M5.
The fitness value is the objective function value (fuzzy completion time)
- select (tournament select)
Use the elite strategy, that is, retain the best individuals in each generation of the population. A roulette wheel approach is not suitable due to ambiguous target values, so a tournament select is used. With replacement, two individuals are randomly selected from the population, and the individual with a smaller fitness value is selected to enter the new population
- Cross (TPX; GPX/GPPX)
Divide the population into two subpopulations, A and B, representing job sequences and machine assignments, respectively. For the machine allocation part, the cross uses two point crossover (two point crossover TPX). For the job sequence part, two kinds of crossovers are used: generalized position crossover (GPX) and generalization of the precedence preservative crossover (GPPX)
GPX: Select substring S from parent1, such as S=1231, find each element in S in parent2 Note that the elements are in order. For example, 1 in S is the second occurrence in parent1, so find the second occurrence of 1 in parent2. After deleting S, parent2 becomes 32414234. Insert S into parent2, the position is equal to the position of S in parent1, that is, pos=4
GPPX: Set a string with a value of [1,2], and the sub-table represents successive selection of genes from parent1(2), if from parent1 A gene is selected in , and its corresponding gene in parent2 is deleted
- Mutation (swap)
For an individual, two genes are randomly selected and exchanged
- algorithm
Randomly generate the initial population P of N individuals;
perform tournament select on P;
divide P into two subpopulations A and B, representing the job sequence and machine allocation part respectively;
perform GPPX or GPX crossover and swap mutation
on population A ; Execute TPX crossover and swap mutation;
merge A and B to generate a new population and calculate fitness value;
iterate until the end
2.2 python code
The operation sequence uses the GPPX crossover operator, the crossover rate=0.8, the mutation rate=0.1, the number of populations=100, the number of iterations=1000, the second coding method, the following is the GA part, the complete code https://github.com/bujibujibiuwang /solve-fuzzy-FJSP-with-GA
import random
from params import get_args
from Shop import Shop
from utils import *
class GA(object):
def __init__(self, args, shop):
self.shop = shop
self.cross_rate = args.cross_rate
self.mutate_rate = args.mutate_rate
self.pop_size = args.pop_size
self.elite_number = args.elite_number
self.chrom_size = self.shop.job_nb * self.shop.op_nb
self.pop = []
def encode(self):
init_pop = []
for _ in range(self.pop_size):
one_string = []
for _ in range(self.shop.op_nb):
one_string += list(np.random.permutation(self.shop.job_nb))
random.shuffle(one_string)
two_string = [random.randint(0, self.shop.machine_nb-1) for _ in range(self.chrom_size)]
individual = np.vstack([one_string, two_string])
init_pop.append(individual)
return np.array(init_pop)
def decode(self, pop1):
fuzzy_fitness = []
certain_fitness = []
for individual in pop1:
fuzzy_completion_time = self.shop.process_decode1(individual)
fuzzy_fitness.append(fuzzy_completion_time)
certain_fitness.append(value(fuzzy_completion_time))
return fuzzy_fitness, certain_fitness
def selection(self, pop2, fuzzy_fitness, certain_fitness):
"""
tournament selection + elite_strategy
"""
pop2 = pop2.tolist()
sorted_pop = sorted(pop2, key=lambda x: certain_fitness[pop2.index(x)], reverse=False)
new_pop = sorted_pop[:self.elite_number]
while len(new_pop) < self.pop_size:
index1, index2 = random.sample(list(range(10, self.pop_size)), 2)
if rank(fuzzy_fitness[index1], fuzzy_fitness[index2]) == fuzzy_fitness[index1]:
new_pop.append(pop[index2])
else:
new_pop.append(pop[index1])
return np.array(new_pop)
def crossover_machine(self, pop_machine):
"""
two point crossover (TPX)
"""
temp = pop_machine.copy().tolist()
new_pop = []
while len(temp) != 0:
parent1, parent2 = random.sample(temp, 2)
temp.remove(parent1)
temp.remove(parent2)
if random.random() < self.cross_rate:
pos1, pos2 = sorted(random.sample(list(range(self.chrom_size)), 2))
offspring1 = parent1[:pos1] + parent2[pos1:pos2] + parent1[pos2:]
offspring2 = parent2[:pos1] + parent1[pos1:pos2] + parent2[pos2:]
else:
offspring1 = parent1
offspring2 = parent2
new_pop.append(offspring1)
new_pop.append(offspring2)
return np.array(new_pop)
def crossover_job(self, pop_job):
"""
generalisation of the precedence preservative crossover (PPX)
"""
temp = pop_job.copy().tolist()
new_pop = []
for parent1 in temp:
if random.random() < self.cross_rate:
new_individual = []
parent2 = pop_job[random.randint(0, self.pop_size-1)].tolist()
string = random.choices([0, 1], k=self.chrom_size)
for choose in string:
if int(choose) == 0:
new_individual.append(parent1[0])
parent2.remove(parent1[0])
parent1 = parent1[1:]
else:
new_individual.append(parent2[0])
parent1.remove(parent2[0])
parent2 = parent2[1:]
new_pop.append(new_individual)
else:
new_pop.append(parent1)
return np.array(new_pop)
def mutation(self, part):
"""
swap
"""
for individual in part:
if random.random() < self.mutate_rate:
pos1, pos2 = random.sample(list(range(self.chrom_size)), 2)
individual[pos1], individual[pos2] = individual[pos2], individual[pos1]
return part
@staticmethod
def elite_strategy(pop3, fitness):
best_fitness = [np.inf, np.inf, np.inf]
best_individual = None
for k, individual in enumerate(pop3):
if rank(fitness[k], best_fitness) == best_fitness:
best_fitness = fitness[k]
best_individual = individual
return best_individual, best_fitness
2.3 Test results
Use the instance1 test in the literature [3], the instance1 in the literature finds the optimal solution [20, 31, 40], and I test the optimal solution [20, 31, 41]
0:old best:[inf, inf, inf], now best:[47, 70, 89], now mean:99.9475
100:old best:[28, 41, 54], now best:[34, 42, 52], now mean:48.41
200:old best:[25, 38, 46], now best:[25, 38, 47], now mean:38.92
300:old best:[25, 36, 45], now best:[25, 36, 45], now mean:38.2425
400:old best:[21, 33, 44], now best:[21, 33, 44], now mean:35.2775
500:old best:[21, 31, 41], now best:[24, 31, 40], now mean:34.475
600:old best:[21, 31, 41], now best:[21, 31, 42], now mean:34.35
700:old best:[19, 31, 42], now best:[19, 31, 42], now mean:33.5275
800:old best:[20, 31, 41], now best:[19, 31, 42], now mean:32.6575
900:old best:[20, 31, 41], now best:[20, 31, 41], now mean:34.175
The fuzzy Gantt chart is as follows and
the iteration curve is as follows