├── .DS_Store ├── Untitled ├── data.txt ├── genetic_toolkit.py ├── genetic_toolkit.pyc └── jobshop.py /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Somnibyte/Job-Shop-Scheduling-Genetic-Algorithm/4111f63993877a207aa8735735b896aff5347acd/.DS_Store -------------------------------------------------------------------------------- /Untitled: -------------------------------------------------------------------------------- 1 | import random 2 | import linecache 3 | import copy 4 | 5 | 6 | class BiologicalProcessManager: 7 | @staticmethod 8 | def pmx(crossover_rate, parentOne, parentTwo): 9 | random_probability = random.random() 10 | 11 | if random_probability < crossover_rate: 12 | return (parentOne, parentTwo) 13 | else: 14 | # Create two crossover points 15 | pivotOne = random.randint(0, len(parentOne.genotype_representation)-1) 16 | pivotTwo = random.randint(pivotOne, len(parentOne.genotype_representation)-1) 17 | #print("FIRST PIVOT POINT:{} ".format(pivotOne)) 18 | #print("SECOND PIVOT POINT: {}".format(pivotTwo)) 19 | # Setup offspring 20 | child_genotype = [0 for i in range(0,len(parentOne.genotype_representation))] 21 | # Copy segment from P1 into child 22 | segmentRange = [x for x in range(pivotOne, pivotTwo+1)] 23 | #print("SEGMENT RANGE: {}".format(segmentRange)) 24 | #print("FIRST INSTRUCTION") 25 | for i, operation in enumerate(parentOne.genotype_representation): 26 | if i in segmentRange: 27 | #print("ADDED op:{} to index:{}".format(operation, i)) 28 | child_genotype[i] = operation 29 | # Copy segment from P2 into child 30 | #print("SECOND INSTRUCTION") 31 | for j, operation in enumerate(parentTwo.genotype_representation): 32 | if j in segmentRange: 33 | # Check P1 34 | op = parentOne.genotype_representation[j] 35 | #print("OP in P1 SIDE: {}".format(op)) 36 | # Check where the element exists in P2 37 | index_of_op = parentTwo.genotype_representation.index(op) 38 | #print("INDEX OF PREV OP IN P2: {}".format(index_of_op)) 39 | # Check if the operation already exists in the child 40 | if operation not in child_genotype: 41 | # Check if position is occupied in the child 42 | if child_genotype[index_of_op] == 0: 43 | #print("ADDED op:{} to index:{}".format(operation, index_of_op)) 44 | child_genotype[index_of_op] = operation 45 | #print("CHILD NOW HAS OP: {} in index: {}".format(child_genotype[index_of_op],index_of_op )) 46 | else: 47 | while(True): 48 | #print("INFINI LOOOOOOOP!") 49 | # Check P1 50 | op = parentOne.genotype_representation[index_of_op] 51 | #print("[WHILE] OP in P1 SIDE: {}".format(op)) 52 | # Check where the element exists in P2 53 | index_of_op = parentTwo.genotype_representation.index(op) 54 | if child_genotype[index_of_op] == 0: 55 | #print("[WHILE] ADDED op:{} to index:{}".format(operation, index_of_op)) 56 | child_genotype[index_of_op] = operation 57 | #print("[WHILE] CHILD NOW HAS OP: {} in index: {}".format(child_genotype[index_of_op],index_of_op )) 58 | break 59 | else: 60 | #print("[WHILE] INDEX OP IS CURRENTLY: {}".format(index_of_op)) 61 | continue 62 | 63 | 64 | 65 | 66 | # Copy the rest P2 into the child 67 | for k, operation in enumerate(parentTwo.genotype_representation): 68 | if k not in segmentRange: 69 | if child_genotype[k] == 0: 70 | #print("ADDED op:{} to index:{}".format(operation, k)) 71 | child_genotype[k] = operation 72 | 73 | # Create offspring 74 | child = Chromosome(parentOne.num_of_ops_ref) 75 | child.genotype_representation = child_genotype 76 | # Generate the phenotype representation of the child 77 | child.generate_phenotype_representation() 78 | # Return the new offspring 79 | #print("---------------------- \n") 80 | #print("FINISHED! RETURNING CHILD") 81 | #print("---------------------- \n") 82 | return (child, 0) 83 | 84 | 85 | 86 | 87 | @staticmethod 88 | def mutate(child): 89 | randindexone = random.randint(0,len(child.genotype_representation)-1) 90 | randindextwo = random.randint(0,len(child.genotype_representation)-1) 91 | if randindexone == randindextwo: 92 | while(True): 93 | randindextwo = random.randint(0,len(child.genotype_representation)-1) 94 | if randindextwo != randindexone: 95 | break 96 | else: 97 | continue 98 | temp = child.genotype_representation[randindexone] 99 | child.genotype_representation[randindexone] = child.genotype_representation[randindextwo] 100 | child.genotype_representation[randindextwo] = temp 101 | child.phenotype_representation = [] 102 | child.generate_phenotype_representation() 103 | 104 | 105 | class JobManager: 106 | jobs = [] 107 | 108 | class Operation: 109 | start_time = 0 # Used for Schedule Building 110 | job_number = None 111 | def __init__(self, number, machine, operation_time): 112 | self.number = number 113 | self.machine = machine 114 | self.operation_time = operation_time 115 | 116 | class Job: 117 | operations = [] 118 | 119 | def __init__(self, number): 120 | self.number = number 121 | 122 | 123 | class ScheduleBuilder: 124 | @staticmethod 125 | def find_makespan(phenotype_rep): 126 | phenotype = copy.deepcopy(phenotype_rep) 127 | current_schedule = [] 128 | j = None 129 | for i, operation in enumerate(phenotype): 130 | ''' DEBUGGING ''' 131 | if len(current_schedule) == 0: 132 | #print("First schedule object") 133 | #print("Operation: #{} job#{} start_time:{}".format(operation.number, operation.job_number, operation.start_time)) 134 | operation.start_time += operation.operation_time 135 | current_schedule.append(operation) 136 | else: 137 | #print('\n') 138 | #print("Operation -> {} | job -> {} | start_time -> {} | machine -> {}".format(operation.number, operation.job_number, operation.start_time, operation.machine)) 139 | #print("Looking for same job...") 140 | #print("Current Schedule count: {} \n".format(len(current_schedule))) 141 | j = len(current_schedule)-1 142 | could_not_find_same_job = False 143 | could_not_find_machine = False 144 | 145 | while(True): 146 | # Find the same job 147 | # If the same job 148 | if current_schedule[j].job_number == operation.job_number: 149 | same_job_but_no_machine_history = False 150 | everything_went_fine = False 151 | #print("Found same job for {} ".format(operation.number)) 152 | # Look up machine history 153 | #print("Looking up machine... \n") 154 | k = len(current_schedule)-1 155 | while(True): 156 | if current_schedule[k].machine == operation.machine: 157 | #print("Found same machine! for op: {} \n".format(operation.number)) 158 | # Do schedule operation 159 | machine_history = current_schedule[k].start_time 160 | #print("Current machine history: {}".format(machine_history)) 161 | if machine_history < current_schedule[j].start_time: 162 | #print("CURRENT SCHED START: {}".format(current_schedule[j].start_time)) 163 | #print("AND MACH HIST: {}".format(machine_history)) 164 | #print("Machine history was less so I'll replace") 165 | operation.start_time = current_schedule[j].start_time 166 | operation.start_time += operation.operation_time 167 | current_schedule.append(operation) 168 | everything_went_fine = True 169 | break 170 | elif machine_history >= current_schedule[j].start_time: 171 | #print("CURRENT SCHED START: {}".format(current_schedule[j].start_time)) 172 | #print("AND MACH HIST: {}".format(machine_history)) 173 | #print("Machine history was greater so I'll continue") 174 | operation.start_time = machine_history 175 | operation.start_time += operation.operation_time 176 | current_schedule.append(operation) 177 | everything_went_fine = True 178 | break 179 | elif k == 0 and current_schedule[0].machine != operation.machine: 180 | same_job_but_no_machine_history = True 181 | break 182 | else: 183 | # continue 184 | #print("I am now CONT to look for a machine!") 185 | k = k - 1 186 | continue 187 | 188 | # Same job, but no other machine history 189 | if same_job_but_no_machine_history: 190 | #print("Same job for op: {}, but no machine history so...ADD!".format(operation.number)) 191 | operation.start_time = current_schedule[j].start_time 192 | operation.start_time += operation.operation_time 193 | current_schedule.append(operation) 194 | break 195 | elif everything_went_fine: 196 | break 197 | 198 | # Couldn't find same job 199 | elif j == 0 and current_schedule[0].job_number != operation.job_number: 200 | #print("Could not find a single job comparison for op: {}".format(operation.number)) 201 | could_not_find_same_job = True 202 | break 203 | else: 204 | j = j - 1 205 | 206 | # Look for any machine history 207 | if could_not_find_same_job: 208 | #print("Since I couldn't find a job for op: {}, I\"ll look for a machine \n".format(operation.number)) 209 | l = len(current_schedule)-1 210 | # Try to find machine history 211 | while(True): 212 | if current_schedule[l].machine == operation.machine: 213 | #print("Found a similar machine for op: {}".format(operation.number)) 214 | operation.start_time = current_schedule[l].start_time 215 | operation.start_time += operation.operation_time 216 | current_schedule.append(operation) 217 | break 218 | elif l == 0 and current_schedule[0].machine != operation.machine: 219 | #print("Could not find a single machine for op: {}".format(operation.number)) 220 | could_not_find_machine = True 221 | break 222 | else: 223 | l = l - 1 224 | 225 | if could_not_find_machine: 226 | #print("Since I couldn't find a machine, I'll just add it to the sys for op: {} \n".format(operation.number)) 227 | operation.start_time += operation.operation_time 228 | current_schedule.append(operation) 229 | 230 | # Reset the flags 231 | could_not_find_machine = False 232 | could_not_find_same_job = False 233 | 234 | # Return the makespan 235 | #print("TOTAL MAKESPAN: {} \n".format(current_schedule[len(current_schedule)-1].start_time)) 236 | return current_schedule[len(current_schedule)-1].start_time 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | class Chromosome: 245 | 246 | fitness = None 247 | 248 | def __init__(self, num_of_operations): 249 | # Create permuation from the number of operations given 250 | self.genotype_representation = [] 251 | self.num_of_ops_ref = num_of_operations 252 | while(len(self.genotype_representation) != num_of_operations): 253 | random_num = random.randint(1, num_of_operations) 254 | if random_num not in self.genotype_representation: 255 | self.genotype_representation.append(random_num) 256 | else: 257 | continue 258 | 259 | def generate_phenotype_representation(self): 260 | self.phenotype_representation = [] 261 | for gene in self.genotype_representation: 262 | for job in JobManager.jobs: 263 | for operation in job.operations: 264 | if operation.number == gene: 265 | self.phenotype_representation.append(operation) 266 | 267 | 268 | def generate_fitness(self): 269 | self.fitness = ScheduleBuilder.find_makespan(self.phenotype_representation) 270 | 271 | 272 | class Population: 273 | 274 | population = [] 275 | 276 | def __init__(self, size): 277 | self.population_size = size 278 | 279 | def select_parents(self,tournament): 280 | ''' 281 | Tournament selection is being used to find two parents 282 | ''' 283 | first_fittest_indiv = None 284 | second_fittest_indiv = None 285 | 286 | for individual in tournament: 287 | # Check if this indivudal is fitter than the current fittist individual 288 | if first_fittest_indiv == None or individual.fitness < first_fittest_indiv.fitness: 289 | first_fittest_indiv = individual 290 | 291 | tournament.remove(first_fittest_indiv) 292 | 293 | for individual in tournament: 294 | # Check if this indivudal is fitter than the current fittist individual 295 | if second_fittest_indiv == None or individual.fitness < second_fittest_indiv.fitness: 296 | second_fittest_indiv = individual 297 | 298 | #print("FIRST: {}, SECOND: {}".format(first_fittest_indiv.fitness,second_fittest_indiv.fitness)) 299 | return (first_fittest_indiv,second_fittest_indiv) 300 | 301 | def initialize_population(self): 302 | ''' 303 | Read from a file and create the chromosomes 304 | ''' 305 | # Open data file 306 | dataFile = open('data.txt', 'r') 307 | 308 | # Obtain the number of jobs and machines 309 | num_of_jobs = int(dataFile.read(2)) 310 | num_of_machines = int(dataFile.read(2)) 311 | num_of_operations = num_of_jobs * num_of_machines 312 | 313 | # Helper variables 314 | list_of_operations = [] 315 | op_counter = 1 316 | 317 | # Loop through the operations in the file 318 | for i, line in enumerate(dataFile): 319 | # Obtain a line of operations and split them. 320 | # Convert each operation time into a number and turn it into an Operation object 321 | # Append the operations into a list 322 | operation_list = [] 323 | list_of_machines = [] 324 | list_of_times = line.split() 325 | for operation_time in list_of_times: 326 | rand_num = random.randint(1, num_of_machines) 327 | if rand_num not in list_of_machines: 328 | list_of_machines.append(rand_num) 329 | time = int(operation_time) 330 | new_operation = Operation(op_counter, rand_num, time) 331 | operation_list.append(new_operation) 332 | op_counter += 1 333 | else: 334 | while(True): 335 | rand_num = random.randint(1, num_of_machines) 336 | if rand_num not in list_of_machines: 337 | list_of_machines.append(rand_num) 338 | time = int(operation_time) 339 | new_operation = Operation( 340 | op_counter, rand_num, time) 341 | operation_list.append(new_operation) 342 | op_counter += 1 343 | break 344 | else: 345 | continue 346 | list_of_operations.append(operation_list) 347 | 348 | # Loop through the operations and place them into the appropriate job 349 | for i, op_list in enumerate(list_of_operations): 350 | new_job = Job(i + 1) 351 | new_job.operations = op_list 352 | for op in op_list: 353 | op.job_number = new_job.number 354 | 355 | # Add the job to the jobManager 356 | JobManager.jobs.append(new_job) 357 | 358 | 359 | # Create the chromsomes and append them to the population 360 | for x in range(0, self.population_size): 361 | # Create a new chromosome 362 | new_chrom = Chromosome(num_of_operations) 363 | print(new_chrom.genotype_representation) 364 | # Generate the phenotype representation of the chromosome 365 | new_chrom.generate_phenotype_representation() 366 | # Generate a fitness for the chromsome 367 | new_chrom.generate_fitness() 368 | # Add the chromsome to the population 369 | self.population.append(new_chrom) 370 | -------------------------------------------------------------------------------- /data.txt: -------------------------------------------------------------------------------- 1 | 20 2 | 20 3 | 64 57 81 98 59 87 93 62 20 14 85 45 47 9 94 9 15 66 1 94 4 | 39 96 88 83 77 58 83 3 78 68 64 97 33 25 47 44 7 60 42 91 5 | 96 66 88 60 22 92 62 14 89 39 94 66 10 53 26 15 65 82 10 27 6 | 93 92 96 70 83 74 31 88 51 57 78 8 7 91 79 18 51 18 99 33 7 | 4 82 40 86 50 54 21 6 54 68 82 20 39 35 68 73 23 30 30 53 8 | 94 58 93 32 91 30 56 27 92 9 78 23 21 60 36 29 95 99 79 76 9 | 93 42 52 42 96 29 61 88 70 16 31 65 83 78 26 50 87 62 14 30 10 | 18 75 20 4 91 68 19 54 85 73 43 24 37 87 66 32 52 9 49 61 11 | 35 99 62 6 62 7 80 3 57 7 85 30 96 91 13 87 82 83 78 56 12 | 85 8 66 88 15 5 59 30 60 41 17 66 89 78 88 69 45 82 6 13 13 | 90 27 1 8 91 80 89 49 32 28 90 93 6 35 73 47 43 75 8 51 14 | 3 84 34 28 60 69 45 67 58 87 65 62 97 20 31 33 33 77 50 80 15 | 48 90 75 96 44 28 21 51 75 17 89 59 56 63 18 17 30 16 7 35 16 | 57 16 42 34 37 26 68 73 5 8 12 87 83 20 97 20 85 61 9 36 17 | 63 11 45 10 33 5 41 47 9 74 33 35 78 12 22 44 8 97 10 86 18 | 33 60 21 96 69 34 94 15 23 84 16 55 50 5 59 35 12 57 11 51 19 | 72 42 4 62 15 27 16 34 8 50 85 12 48 5 25 40 81 46 67 25 20 | 83 92 25 40 21 4 43 38 60 24 3 28 86 68 55 91 97 19 73 20 21 | 28 81 46 98 46 29 96 12 71 32 64 39 16 97 99 49 75 7 79 80 22 | 71 9 11 8 4 47 93 82 6 49 7 24 92 13 86 80 34 75 35 29 23 | -------------------------------------------------------------------------------- /genetic_toolkit.py: -------------------------------------------------------------------------------- 1 | import random 2 | import linecache 3 | import copy 4 | 5 | 6 | class BiologicalProcessManager: 7 | @staticmethod 8 | def pmx(crossover_rate, parentOne, parentTwo): 9 | random_probability = random.random() 10 | 11 | if random_probability < crossover_rate: 12 | return (parentOne, parentTwo) 13 | else: 14 | # Create two crossover points 15 | pivotOne = random.randint(0, len(parentOne.genotype_representation)-1) 16 | pivotTwo = random.randint(pivotOne, len(parentOne.genotype_representation)-1) 17 | #print("FIRST PIVOT POINT:{} ".format(pivotOne)) 18 | #print("SECOND PIVOT POINT: {}".format(pivotTwo)) 19 | # Setup offspring 20 | child_genotype = [0 for i in range(0,len(parentOne.genotype_representation))] 21 | # Copy segment from P1 into child 22 | segmentRange = [x for x in range(pivotOne, pivotTwo+1)] 23 | #print("SEGMENT RANGE: {}".format(segmentRange)) 24 | #print("FIRST INSTRUCTION") 25 | for i, operation in enumerate(parentOne.genotype_representation): 26 | if i in segmentRange: 27 | #print("ADDED op:{} to index:{}".format(operation, i)) 28 | child_genotype[i] = operation 29 | # Copy segment from P2 into child 30 | #print("SECOND INSTRUCTION") 31 | for j, operation in enumerate(parentTwo.genotype_representation): 32 | if j in segmentRange: 33 | # Check P1 34 | op = parentOne.genotype_representation[j] 35 | #print("OP in P1 SIDE: {}".format(op)) 36 | # Check where the element exists in P2 37 | index_of_op = parentTwo.genotype_representation.index(op) 38 | #print("INDEX OF PREV OP IN P2: {}".format(index_of_op)) 39 | # Check if the operation already exists in the child 40 | if operation not in child_genotype: 41 | # Check if position is occupied in the child 42 | if child_genotype[index_of_op] == 0: 43 | #print("ADDED op:{} to index:{}".format(operation, index_of_op)) 44 | child_genotype[index_of_op] = operation 45 | #print("CHILD NOW HAS OP: {} in index: {}".format(child_genotype[index_of_op],index_of_op )) 46 | else: 47 | while(True): 48 | #print("INFINI LOOOOOOOP!") 49 | # Check P1 50 | op = parentOne.genotype_representation[index_of_op] 51 | #print("[WHILE] OP in P1 SIDE: {}".format(op)) 52 | # Check where the element exists in P2 53 | index_of_op = parentTwo.genotype_representation.index(op) 54 | if child_genotype[index_of_op] == 0: 55 | #print("[WHILE] ADDED op:{} to index:{}".format(operation, index_of_op)) 56 | child_genotype[index_of_op] = operation 57 | #print("[WHILE] CHILD NOW HAS OP: {} in index: {}".format(child_genotype[index_of_op],index_of_op )) 58 | break 59 | else: 60 | #print("[WHILE] INDEX OP IS CURRENTLY: {}".format(index_of_op)) 61 | continue 62 | 63 | 64 | 65 | 66 | # Copy the rest P2 into the child 67 | for k, operation in enumerate(parentTwo.genotype_representation): 68 | if k not in segmentRange: 69 | if child_genotype[k] == 0: 70 | #print("ADDED op:{} to index:{}".format(operation, k)) 71 | child_genotype[k] = operation 72 | 73 | # Create offspring 74 | child = Chromosome(parentOne.num_of_ops_ref) 75 | child.genotype_representation = child_genotype 76 | # Generate the phenotype representation of the child 77 | child.generate_phenotype_representation() 78 | # Return the new offspring 79 | #print("---------------------- \n") 80 | #print("FINISHED! RETURNING CHILD") 81 | #print("---------------------- \n") 82 | return (child, 0) 83 | 84 | 85 | 86 | 87 | @staticmethod 88 | def mutate(child): 89 | randindexone = random.randint(0,len(child.genotype_representation)-1) 90 | randindextwo = random.randint(0,len(child.genotype_representation)-1) 91 | if randindexone == randindextwo: 92 | while(True): 93 | randindextwo = random.randint(0,len(child.genotype_representation)-1) 94 | if randindextwo != randindexone: 95 | break 96 | else: 97 | continue 98 | temp = child.genotype_representation[randindexone] 99 | child.genotype_representation[randindexone] = child.genotype_representation[randindextwo] 100 | child.genotype_representation[randindextwo] = temp 101 | child.phenotype_representation = [] 102 | child.generate_phenotype_representation() 103 | 104 | 105 | class JobManager: 106 | jobs = [] 107 | 108 | class Operation: 109 | start_time = 0 # Used for Schedule Building 110 | job_number = None 111 | def __init__(self, number, machine, operation_time): 112 | self.number = number 113 | self.machine = machine 114 | self.operation_time = operation_time 115 | 116 | class Job: 117 | operations = [] 118 | 119 | def __init__(self, number): 120 | self.number = number 121 | 122 | 123 | class ScheduleBuilder: 124 | @staticmethod 125 | def find_makespan(phenotype_rep): 126 | phenotype = copy.deepcopy(phenotype_rep) 127 | current_schedule = [] 128 | j = None 129 | for i, operation in enumerate(phenotype): 130 | ''' DEBUGGING ''' 131 | #for op in current_schedule: 132 | #print("---------") 133 | #print("Operation:{} job:{} current_time:{} machine: {} \n".format(op.number, op.job_number, op.start_time, op.machine)) 134 | 135 | if len(current_schedule) == 0: 136 | #print("First schedule object") 137 | #print("Operation: #{} job#{} start_time:{}".format(operation.number, operation.job_number, operation.start_time)) 138 | operation.start_time += operation.operation_time 139 | current_schedule.append(operation) 140 | else: 141 | #print('\n') 142 | #print("Operation -> {} | job -> {} | start_time -> {} | machine -> {}".format(operation.number, operation.job_number, operation.start_time, operation.machine)) 143 | #print("Looking for same job...") 144 | #print("Current Schedule count: {} \n".format(len(current_schedule))) 145 | j = len(current_schedule)-1 146 | could_not_find_same_job = False 147 | could_not_find_machine = False 148 | 149 | while(True): 150 | # Find the same job 151 | # If the same job 152 | if current_schedule[j].job_number == operation.job_number: 153 | same_job_but_no_machine_history = False 154 | everything_went_fine = False 155 | #print("Found same job for {} ".format(operation.number)) 156 | # Look up machine history 157 | #print("Looking up machine... \n") 158 | k = len(current_schedule)-1 159 | while(True): 160 | if current_schedule[k].machine == operation.machine: 161 | #print("Found same machine! for op: {} \n".format(operation.number)) 162 | # Do schedule operation 163 | machine_history = current_schedule[k].start_time 164 | #print("Current machine history: {}".format(machine_history)) 165 | if machine_history < current_schedule[j].start_time: 166 | #print("CURRENT SCHED START: {}".format(current_schedule[j].start_time)) 167 | #print("AND MACH HIST: {}".format(machine_history)) 168 | #print("Machine history was less so I'll replace") 169 | operation.start_time = current_schedule[j].start_time 170 | operation.start_time += operation.operation_time 171 | current_schedule.append(operation) 172 | everything_went_fine = True 173 | break 174 | elif machine_history >= current_schedule[j].start_time: 175 | #print("CURRENT SCHED START: {}".format(current_schedule[j].start_time)) 176 | #print("AND MACH HIST: {}".format(machine_history)) 177 | #print("Machine history was greater so I'll continue") 178 | operation.start_time = machine_history 179 | operation.start_time += operation.operation_time 180 | current_schedule.append(operation) 181 | everything_went_fine = True 182 | break 183 | elif k == 0 and current_schedule[0].machine != operation.machine: 184 | same_job_but_no_machine_history = True 185 | break 186 | else: 187 | # continue 188 | #print("I am now CONT to look for a machine!") 189 | k = k - 1 190 | continue 191 | 192 | # Same job, but no other machine history 193 | if same_job_but_no_machine_history: 194 | #print("Same job for op: {}, but no machine history so...ADD!".format(operation.number)) 195 | operation.start_time = current_schedule[j].start_time 196 | operation.start_time += operation.operation_time 197 | current_schedule.append(operation) 198 | break 199 | elif everything_went_fine: 200 | break 201 | 202 | # Couldn't find same job 203 | elif j == 0 and current_schedule[0].job_number != operation.job_number: 204 | #print("Could not find a single job comparison for op: {}".format(operation.number)) 205 | could_not_find_same_job = True 206 | break 207 | else: 208 | j = j - 1 209 | 210 | # Look for any machine history 211 | if could_not_find_same_job: 212 | #print("Since I couldn't find a job for op: {}, I\"ll look for a machine \n".format(operation.number)) 213 | l = len(current_schedule)-1 214 | # Try to find machine history 215 | while(True): 216 | if current_schedule[l].machine == operation.machine: 217 | #print("Found a similar machine for op: {}".format(operation.number)) 218 | operation.start_time = current_schedule[l].start_time 219 | operation.start_time += operation.operation_time 220 | current_schedule.append(operation) 221 | break 222 | elif l == 0 and current_schedule[0].machine != operation.machine: 223 | #print("Could not find a single machine for op: {}".format(operation.number)) 224 | could_not_find_machine = True 225 | break 226 | else: 227 | l = l - 1 228 | 229 | if could_not_find_machine: 230 | #print("Since I couldn't find a machine, I'll just add it to the sys for op: {} \n".format(operation.number)) 231 | operation.start_time += operation.operation_time 232 | current_schedule.append(operation) 233 | 234 | # Reset the flags 235 | could_not_find_machine = False 236 | could_not_find_same_job = False 237 | 238 | # Return the makespan 239 | largest_time = None 240 | for operation in current_schedule: 241 | if largest_time == None or operation.start_time > largest_time: 242 | largest_time = operation.start_time 243 | 244 | return largest_time 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | class Chromosome: 253 | 254 | fitness = None 255 | 256 | def __init__(self, num_of_operations): 257 | # Create permuation from the number of operations given 258 | self.genotype_representation = [] 259 | self.num_of_ops_ref = num_of_operations 260 | while(len(self.genotype_representation) != num_of_operations): 261 | random_num = random.randint(1, num_of_operations) 262 | if random_num not in self.genotype_representation: 263 | self.genotype_representation.append(random_num) 264 | else: 265 | continue 266 | 267 | def generate_phenotype_representation(self): 268 | self.phenotype_representation = [] 269 | for gene in self.genotype_representation: 270 | for job in JobManager.jobs: 271 | for operation in job.operations: 272 | if operation.number == gene: 273 | self.phenotype_representation.append(operation) 274 | 275 | 276 | def generate_fitness(self): 277 | self.fitness = ScheduleBuilder.find_makespan(self.phenotype_representation) 278 | 279 | 280 | class Population: 281 | 282 | population = [] 283 | 284 | def __init__(self, size): 285 | self.population_size = size 286 | 287 | def select_parents(self,tournament): 288 | ''' 289 | Tournament selection is being used to find two parents 290 | ''' 291 | first_fittest_indiv = None 292 | second_fittest_indiv = None 293 | 294 | for individual in tournament: 295 | # Check if this indivudal is fitter than the current fittist individual 296 | if first_fittest_indiv == None or individual.fitness < first_fittest_indiv.fitness: 297 | first_fittest_indiv = individual 298 | 299 | tournament.remove(first_fittest_indiv) 300 | 301 | for individual in tournament: 302 | # Check if this indivudal is fitter than the current fittist individual 303 | if second_fittest_indiv == None or individual.fitness < second_fittest_indiv.fitness: 304 | second_fittest_indiv = individual 305 | 306 | #print("FIRST: {}, SECOND: {}".format(first_fittest_indiv.fitness,second_fittest_indiv.fitness)) 307 | return (first_fittest_indiv,second_fittest_indiv) 308 | 309 | def initialize_population(self): 310 | ''' 311 | Read from a file and create the chromosomes 312 | ''' 313 | # Open data file 314 | dataFile = open('data.txt', 'r') 315 | 316 | # Obtain the number of jobs and machines 317 | num_of_jobs = int(dataFile.read(3)) 318 | print("NUMBER OF JOBS: {}".format(num_of_jobs)) 319 | num_of_machines = int(dataFile.read(3)) 320 | print("NUM OF MACH: {}".format(num_of_machines)) 321 | num_of_operations = num_of_jobs * num_of_machines 322 | 323 | # Helper variables 324 | list_of_operations = [] 325 | op_counter = 1 326 | 327 | # Loop through the operations in the file 328 | for i, line in enumerate(dataFile): 329 | # Obtain a line of operations and split them. 330 | # Convert each operation time into a number and turn it into an Operation object 331 | # Append the operations into a list 332 | operation_list = [] 333 | list_of_machines = [] 334 | list_of_times = line.split() 335 | for operation_time in list_of_times: 336 | rand_num = random.randint(1, num_of_machines) 337 | if rand_num not in list_of_machines: 338 | list_of_machines.append(rand_num) 339 | time = int(operation_time) 340 | new_operation = Operation(op_counter, rand_num, time) 341 | operation_list.append(new_operation) 342 | op_counter += 1 343 | else: 344 | while(True): 345 | rand_num = random.randint(1, num_of_machines) 346 | if rand_num not in list_of_machines: 347 | list_of_machines.append(rand_num) 348 | time = int(operation_time) 349 | new_operation = Operation( 350 | op_counter, rand_num, time) 351 | operation_list.append(new_operation) 352 | op_counter += 1 353 | break 354 | else: 355 | continue 356 | list_of_operations.append(operation_list) 357 | 358 | # Loop through the operations and place them into the appropriate job 359 | for i, op_list in enumerate(list_of_operations): 360 | new_job = Job(i + 1) 361 | new_job.operations = op_list 362 | for op in op_list: 363 | op.job_number = new_job.number 364 | 365 | # Add the job to the jobManager 366 | JobManager.jobs.append(new_job) 367 | 368 | 369 | # Create the chromsomes and append them to the population 370 | for x in range(0, self.population_size): 371 | # Create a new chromosome 372 | new_chrom = Chromosome(num_of_operations) 373 | #print(new_chrom.genotype_representation) 374 | # Generate the phenotype representation of the chromosome 375 | new_chrom.generate_phenotype_representation() 376 | # Generate a fitness for the chromsome 377 | new_chrom.generate_fitness() 378 | # Add the chromsome to the population 379 | self.population.append(new_chrom) 380 | -------------------------------------------------------------------------------- /genetic_toolkit.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Somnibyte/Job-Shop-Scheduling-Genetic-Algorithm/4111f63993877a207aa8735735b896aff5347acd/genetic_toolkit.pyc -------------------------------------------------------------------------------- /jobshop.py: -------------------------------------------------------------------------------- 1 | # Author: Guled Ahmed 2 | # Problem: Job Shop Scheduling 3 | from genetic_toolkit import Population, Chromosome, BiologicalProcessManager, JobManager 4 | import random 5 | 6 | 7 | # Population Initialization 8 | population = Population(100) 9 | population.initialize_population() 10 | 11 | # Rates 12 | crossover_rate = 0.70 13 | 14 | 15 | 16 | # Main Algorithm 17 | generation_counter = 0 18 | while(generation_counter != 200): 19 | current_population_fitnesses = [chromosome.fitness for chromosome in population.population] 20 | print("CURRENT GEN FITNESS: {} ".format(current_population_fitnesses)) 21 | new_gen = [] 22 | while(len(new_gen) <= population.population_size): 23 | # Create tournament for tournament selection process 24 | tournament = [population.population[random.randint(1, population.population_size-1)] for individual in range(1, population.population_size)] 25 | 26 | # Obtain two parents from the process of tournament selection 27 | parent_one, parent_two = population.select_parents(tournament) 28 | 29 | # Create the offspring from those two parents 30 | child_one,child_two = BiologicalProcessManager.pmx(crossover_rate,parent_one,parent_two) 31 | 32 | # Our algorithm produces only one child or returns back to parents 33 | # Here we check if only one child was returned or not 34 | if child_two == 0: 35 | 36 | # Mutate the child 37 | BiologicalProcessManager.mutate(child_one) 38 | 39 | # Evaluate the child 40 | child_one.generate_fitness() 41 | 42 | # Add the child to the new generation of chromosomes 43 | new_gen.append(child_one) 44 | 45 | else: 46 | # Since the crossover returned back two parents, we'll just put them back into the pop 47 | new_gen.append(child_one) 48 | new_gen.append(child_two) 49 | 50 | # Replace old generation with the new one 51 | population.population = new_gen 52 | generation_counter += 1 53 | --------------------------------------------------------------------------------