Skip to content

Commit 33f6086

Browse files
committed
Adds genetic solve method
1 parent 60fa8fc commit 33f6086

File tree

1 file changed

+103
-23
lines changed

1 file changed

+103
-23
lines changed

flowshop.py

Lines changed: 103 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
#!/usr/bin/env python
22
# -*- coding: utf-8 -*-
33

4-
import numpy as np
54
from itertools import permutations
5+
import numpy as np
66
from functools import lru_cache
7+
from geneticFunctions import *
8+
79

810
class 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

324403
if __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

Comments
 (0)