1+ # -*-coding:utf-8 -*
2+ import os
3+ import random
4+ import time
5+ from functools import reduce
6+ import numpy as np
7+
8+
9+ def calc_makespan (solution , proccessing_time , number_of_jobs , number_of_machines ):
10+ # list for the time passed until the finishing of the job
11+ cost = [0 ] * number_of_jobs
12+ # for each machine, total time passed will be updated
13+ for machine_no in range (0 , number_of_machines ):
14+ for slot in range (number_of_jobs ):
15+ # time passed so far until the task starts to process
16+ cost_so_far = cost [slot ]
17+ if slot > 0 :
18+ cost_so_far = max (cost [slot - 1 ], cost [slot ])
19+ cost [slot ] = cost_so_far + proccessing_time [solution [slot ]][machine_no ]
20+ return cost [number_of_jobs - 1 ]
21+
22+ def initialize_population (population_size , number_of_jobs ):
23+ population = []
24+ i = 0
25+ while i < population_size :
26+ individual = list (np .random .permutation (number_of_jobs ))
27+ if individual not in population :
28+ population .append (individual )
29+ i += 1
30+
31+ return population
32+
33+ # Two-point crossover is that the set of jobs between
34+ # two randomly selected points is always inherited from one parent to the child,
35+ # and the other jobs are placed in the same manner as the one-point crossover.
36+ def crossover (parents ):
37+ parent1 = parents [0 ]
38+ parent2 = parents [1 ]
39+ length_of_parent = len (parent1 )
40+ first_point = int (length_of_parent / 2 - length_of_parent / 4 )
41+ second_point = int (length_of_parent - first_point )
42+ intersect = parent1 [first_point :second_point ]
43+
44+ child = []
45+ index = 0
46+ for pos2 in range (len (parent2 )):
47+ if first_point <= index < second_point :
48+ child .extend (intersect )
49+ index = second_point
50+ if parent2 [pos2 ] not in intersect :
51+ child .append (parent2 [pos2 ])
52+ index += 1
53+
54+ return child
55+
56+ # apply mutation to an existing solution using swap move operator
57+ def mutation (solution ):
58+ # copy the solution
59+ mutated_solution = list (solution )
60+ solution_length = len (solution )
61+ # pick 2 positions to swap randomly
62+ swap_positions = list (np .random .permutation (np .arange (solution_length ))[:2 ])
63+ first_job = solution [swap_positions [0 ]]
64+ second_job = solution [swap_positions [1 ]]
65+ mutated_solution [swap_positions [0 ]] = second_job
66+ mutated_solution [swap_positions [1 ]] = first_job
67+ return mutated_solution
68+
69+ # Selects parent by binary tournament method
70+ def select_parent (population , processing_time , number_of_jobs , number_of_machines ):
71+ parent_pairs = []
72+ # randomly choose how many parent pairs will be selected
73+ parent_pair_count = random .randint (2 , int (len (population )/ 2 ))
74+ for k in range (parent_pair_count ):
75+ parent1 = binary_tournament (number_of_jobs , number_of_machines , population , processing_time )
76+ parent2 = binary_tournament (number_of_jobs , number_of_machines , population , processing_time )
77+ if parent1 != parent2 and (parent1 , parent2 ) not in parent_pairs :
78+ parent_pairs .append ((parent1 , parent2 ))
79+ return parent_pairs
80+
81+ def binary_tournament (number_of_jobs , number_of_machines , population , processing_time ):
82+ parent = []
83+ candidates = random .sample (population , 2 )
84+ makespan1 = calc_makespan (candidates [0 ], processing_time , number_of_jobs , number_of_machines )
85+ makespan2 = calc_makespan (candidates [1 ], processing_time , number_of_jobs , number_of_machines )
86+ if makespan1 < makespan2 :
87+ parent = candidates [0 ]
88+ else :
89+ parent = candidates [1 ]
90+ return parent
91+
92+ def update_population (population , children ,processing_time ,no_of_jobs ,no_of_machines ):
93+ costed_population = []
94+ for individual in population :
95+ ind_makespan = (calc_makespan (individual , processing_time , no_of_jobs , no_of_machines ), individual )
96+ costed_population .append (ind_makespan )
97+ costed_population .sort (key = lambda x : x [0 ], reverse = True )
98+
99+ costed_children = []
100+ for individual in children :
101+ ind_makespan = (calc_makespan (individual , processing_time , no_of_jobs , no_of_machines ), individual )
102+ costed_children .append (ind_makespan )
103+ costed_children .sort (key = lambda x : x [0 ])
104+ for child in costed_children :
105+ if child not in population :
106+ population .append (individual )
107+ population .remove (costed_population [0 ][1 ])
108+ break
0 commit comments