11#!/usr/bin/env python
22# -*- coding: utf-8 -*-
33
4- import numpy as np
54from itertools import permutations
5+ import numpy as np
66from functools import lru_cache
7+ from geneticFunctions import *
8+
79
810class Flowshop (object ):
911 """
@@ -27,7 +29,6 @@ def __init__(self, data=None, nb_machines=2, nb_jobs=6):
2729 self .data = RandomFlowshop (
2830 self .nb_machines , self .nb_jobs ).get_data ()
2931
30-
3132 def solve_johnson (self ):
3233 """Solves a permutation flowshop problem using johnson's rule for a permutation problem of 2 machines and N jobs
3334
@@ -86,33 +87,26 @@ def solve_johnson(self):
8687 @staticmethod
8788 def johnson_seq (data ):
8889 # data matrix must have only two machines
89-
9090 nb_machines = len (data )
9191 nb_jobs = len (data [0 ])
92-
9392 machine_1_sequence = [j for j in range (
9493 nb_jobs ) if data [0 ][j ] <= data [1 ][j ]]
9594 machine_1_sequence .sort (key = lambda x : data [0 ][x ])
9695 machine_2_sequence = [j for j in range (
9796 nb_jobs ) if data [0 ][j ] > data [1 ][j ]]
9897 machine_2_sequence .sort (key = lambda x : data [1 ][x ], reverse = True )
99-
10098 seq = machine_1_sequence + machine_2_sequence
101-
10299 return seq
103100
104-
105-
106101 def cds (self ):
107102 raise NotImplementedError
108-
109103
110104 def palmer_heuristic (self ):
111105 """solves an N machines M jobs pfsp problem using Palmer's Heuristic
112-
113106 Returns:
114107 tuple -- a tuple containing the job sequence, scheduled jobs and optimal makespan.
115108 """
109+
116110 def palmer_f (x ): return - (self .nb_machines - (2 * x - 1 ))
117111 weights = list (map (palmer_f , range (1 , self .nb_machines + 1 )))
118112 ws = []
@@ -169,10 +163,8 @@ def _get_makespan(self, seq, data):
169163 s_t = max (c [m_id ][i ][1 ], c [m_id - 1 ][i + 1 ][1 ])
170164 e_t = s_t + data [m_id ][job_id ]
171165 c [m_id ][i + 1 ] = (s_t , e_t )
172-
173166 return c [self .nb_machines - 1 ][- 1 ][1 ]
174167
175-
176168 def neh_heuristic (self ):
177169 sums = []
178170 for job_id in range (self .nb_jobs ):
@@ -186,7 +178,7 @@ def neh_heuristic(self):
186178 for i in range (0 , len (seq ) + 1 ):
187179 cand = seq [:i ] + [job [0 ]] + seq [i :]
188180 cands .append ((cand , self ._get_makespan (cand , self .data )))
189- seq = min (cands , key = lambda x : x [1 ])[0 ]
181+ seq = min (cands , key = lambda x : x [1 ])[0 ]
190182
191183 schedules = np .zeros ((self .nb_machines , self .nb_jobs ), dtype = dict )
192184 # schedule first job alone first
@@ -208,16 +200,14 @@ def neh_heuristic(self):
208200 schedules [0 ][index + 1 ] = task
209201 for m_id in range (1 , self .nb_machines ):
210202 start_t = max (schedules [m_id ][index ]["end_time" ],
211- schedules [m_id - 1 ][index + 1 ]["end_time" ])
203+ schedules [m_id - 1 ][index + 1 ]["end_time" ])
212204 end_t = start_t + self .data [m_id ][job_id ]
213205 task = {"name" : "job_{}" .format (
214206 job_id + 1 ), "start_time" : start_t , "end_time" : end_t }
215207 schedules [m_id ][index + 1 ] = task
216-
217208 max_mkspn = int (schedules [self .nb_machines - 1 ][- 1 ]["end_time" ])
218209 return seq , schedules , max_mkspn
219210
220-
221211 @lru_cache (maxsize = 128 )
222212 def brute_force_exact (self ):
223213 jobs_perm = permutations (range (self .nb_jobs ))
@@ -242,13 +232,102 @@ def brute_force_exact(self):
242232 schedules [0 ][index + 1 ] = task
243233 for m_id in range (1 , self .nb_machines ):
244234 start_t = max (schedules [m_id ][index ]["end_time" ],
245- schedules [m_id - 1 ][index + 1 ]["end_time" ])
235+ schedules [m_id - 1 ][index + 1 ]["end_time" ])
246236 end_t = start_t + self .data [m_id ][job_id ]
247237 task = {"name" : "job_{}" .format (
248238 job_id + 1 ), "start_time" : start_t , "end_time" : end_t }
249239 schedules [m_id ][index + 1 ] = task
250240 makespan = int (schedules [self .nb_machines - 1 ][- 1 ]["end_time" ])
251-
241+ return seq , schedules , makespan
242+
243+ def genetic_algorithm (self ):
244+ optimal = [4534 , 920 , 1302 ]
245+ opt = 0
246+ no_of_jobs , no_of_machines = self .nb_jobs , self .nb_machines
247+ processing_time = []
248+ for i in range (no_of_jobs ):
249+ temp = []
250+ for j in range (no_of_machines ):
251+ temp .append (self .data [j ][i ])
252+ processing_time .append (temp )
253+ # generate an initial population proportional to no_of_jobs
254+ number_of_population = no_of_jobs ** 2
255+ no_of_iterations = 5000
256+ p_crossover = 1.0
257+ p_mutation = 1.0
258+
259+ # Initialize population
260+ population = initialize_population (
261+ number_of_population , no_of_jobs )
262+
263+ for evaluation in range (no_of_iterations ):
264+ # Select parents
265+ parent_list = select_parent (
266+ population , processing_time , no_of_jobs , no_of_machines )
267+ childs = []
268+
269+ # Apply crossover to generate children
270+ for parents in parent_list :
271+ r = np .random .rand ()
272+ if r < p_crossover :
273+ childs .append (crossover (parents ))
274+ else :
275+ if r < 0.5 :
276+ childs .append (parents [0 ])
277+ else :
278+ childs .append (parents [1 ])
279+
280+ # Apply mutation operation to change the order of the n-jobs
281+ mutated_childs = []
282+ for child in childs :
283+ r = np .random .rand ()
284+ if r < p_mutation :
285+ mutated_child = mutation (child )
286+ mutated_childs .append (mutated_child )
287+
288+ childs .extend (mutated_childs )
289+ if len (childs ) > 0 :
290+ update_population (
291+ population , childs , processing_time , no_of_jobs , no_of_machines )
292+
293+ costed_population = []
294+ for individual in population :
295+ ind_makespan = (calc_makespan (
296+ individual , processing_time , no_of_jobs , no_of_machines ), individual )
297+ costed_population .append (ind_makespan )
298+ costed_population .sort (key = lambda x : x [0 ])
299+
300+ seq = list (map (int , costed_population [0 ][1 ]))
301+ makespan = self ._get_makespan (seq , self .data )
302+
303+ schedules = np .zeros ((self .nb_machines , self .nb_jobs ), dtype = dict )
304+ # schedule first job alone first
305+ task = {"name" : "job_{}" .format (
306+ seq [0 ]+ 1 ), "start_time" : 0 , "end_time" : self .data [0 ][seq [0 ]]}
307+ schedules [0 ][0 ] = task
308+ for m_id in range (1 , self .nb_machines ):
309+ start_t = schedules [m_id - 1 ][0 ]["end_time" ]
310+ end_t = start_t + self .data [m_id ][0 ]
311+ task = {"name" : "job_{}" .format (
312+ seq [0 ]+ 1 ), "start_time" : start_t , "end_time" : end_t }
313+ schedules [m_id ][0 ] = task
314+
315+ for index , job_id in enumerate (seq [1 ::]):
316+ start_t = schedules [0 ][index ]["end_time" ]
317+ end_t = start_t + self .data [0 ][job_id ]
318+ task = {"name" : "job_{}" .format (
319+ job_id + 1 ), "start_time" : start_t , "end_time" : end_t }
320+ schedules [0 ][index + 1 ] = task
321+ for m_id in range (1 , self .nb_machines ):
322+ start_t = max (schedules [m_id ][index ]["end_time" ],
323+ schedules [m_id - 1 ][index + 1 ]["end_time" ])
324+ end_t = start_t + self .data [m_id ][job_id ]
325+ task = {"name" : "job_{}" .format (
326+ job_id + 1 ), "start_time" : start_t , "end_time" : end_t }
327+ schedules [m_id ][index + 1 ] = task
328+
329+ #print(seq, makespan)
330+ # print(schedules)
252331 return seq , schedules , makespan
253332
254333
@@ -322,14 +401,15 @@ def get_problem_instance(self):
322401
323402
324403if __name__ == "__main__" :
325- random_problem = RandomFlowshop (5 , 8 )
404+ random_problem = RandomFlowshop (4 , 5 )
326405 random_problem_instance = random_problem .get_problem_instance ()
327- seq = random_problem_instance .cds ()
406+ seq , _ , g_mkspan = random_problem_instance .genetic_algorithm ()
328407 b_seq , b_scheds , b_opt_makespan = random_problem_instance .brute_force_exact ()
329- print (b_opt_makespan , seq )
330- #print(seq)
408+ print (type (seq ))
409+ print (type (seq [0 ]))
410+ print (b_opt_makespan , g_mkspan )
411+ # print(seq)
331412 #print("Brute Force: {}, Palmer heuristic: {}".format(b_seq, seq))
332-
333413 #seq, jobs, opt_makespan = random_problem_instance.solve_johnson()
334414 # print("Sequence: {} \nJobs on Machine 1: \n {} \n Jobs on machine 2:\n {} \n".format(
335415 # seq, jobs[0], jobs[1]))
0 commit comments