GowriHari commited on
Commit
b75af81
·
0 Parent(s):

Duplicate from GowriHari/OptimizationJSSP

Browse files
Files changed (8) hide show
  1. .gitattributes +34 -0
  2. Demo_data.xlsx +0 -0
  3. GA.py +299 -0
  4. MIP.py +133 -0
  5. OR_tools.py +148 -0
  6. README.md +14 -0
  7. app.py +55 -0
  8. requirements.txt +28 -0
.gitattributes ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ *.7z filter=lfs diff=lfs merge=lfs -text
2
+ *.arrow filter=lfs diff=lfs merge=lfs -text
3
+ *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bz2 filter=lfs diff=lfs merge=lfs -text
5
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
6
+ *.ftz filter=lfs diff=lfs merge=lfs -text
7
+ *.gz filter=lfs diff=lfs merge=lfs -text
8
+ *.h5 filter=lfs diff=lfs merge=lfs -text
9
+ *.joblib filter=lfs diff=lfs merge=lfs -text
10
+ *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
+ *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
+ *.model filter=lfs diff=lfs merge=lfs -text
13
+ *.msgpack filter=lfs diff=lfs merge=lfs -text
14
+ *.npy filter=lfs diff=lfs merge=lfs -text
15
+ *.npz filter=lfs diff=lfs merge=lfs -text
16
+ *.onnx filter=lfs diff=lfs merge=lfs -text
17
+ *.ot filter=lfs diff=lfs merge=lfs -text
18
+ *.parquet filter=lfs diff=lfs merge=lfs -text
19
+ *.pb filter=lfs diff=lfs merge=lfs -text
20
+ *.pickle filter=lfs diff=lfs merge=lfs -text
21
+ *.pkl filter=lfs diff=lfs merge=lfs -text
22
+ *.pt filter=lfs diff=lfs merge=lfs -text
23
+ *.pth filter=lfs diff=lfs merge=lfs -text
24
+ *.rar filter=lfs diff=lfs merge=lfs -text
25
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
26
+ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
+ *.tar.* filter=lfs diff=lfs merge=lfs -text
28
+ *.tflite filter=lfs diff=lfs merge=lfs -text
29
+ *.tgz filter=lfs diff=lfs merge=lfs -text
30
+ *.wasm filter=lfs diff=lfs merge=lfs -text
31
+ *.xz filter=lfs diff=lfs merge=lfs -text
32
+ *.zip filter=lfs diff=lfs merge=lfs -text
33
+ *.zst filter=lfs diff=lfs merge=lfs -text
34
+ *tfevents* filter=lfs diff=lfs merge=lfs -text
Demo_data.xlsx ADDED
Binary file (10.3 kB). View file
 
GA.py ADDED
@@ -0,0 +1,299 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # importing required modules
2
+ import pandas as pd
3
+ import numpy as np
4
+ import matplotlib.pyplot as plt
5
+ import chart_studio.plotly as py
6
+ import plotly.figure_factory as ff
7
+ import datetime
8
+ from datetime import timedelta
9
+ import time
10
+ import copy
11
+ import openpyxl as xl
12
+ import json
13
+
14
+ '''Defining Functions for GA Algorithm'''
15
+ def input_file(input):
16
+ data = data_excel_json(input)
17
+ op,img,schedule_df = job_schedule(data_dict=data)
18
+ return op,img,schedule_df
19
+
20
+ def data_excel_json(excel_sheet):
21
+ """ convert excel into json """
22
+ data_excel = xl.load_workbook(excel_sheet)
23
+ data = {}
24
+ sheet_name = data_excel.sheetnames
25
+ for sheet in sheet_name:
26
+ wb_sheet = data_excel[sheet]
27
+ cell_values = wb_sheet.values
28
+ df = pd.DataFrame(cell_values, columns=next(cell_values))
29
+ df.iloc[:, 0] = df.iloc[:, 0].apply(lambda x : x.strip())
30
+ df.index = df.iloc[:, 0]
31
+ df.drop(columns = df.columns[0], inplace=True)
32
+ data[sheet] = df.T.to_dict()
33
+ #print("function - data_excel_json: ", data)
34
+ return data
35
+
36
+ def json_to_df(json_data):
37
+ """ convert json into excel """
38
+ dict_data = {}
39
+ for key in json_data.keys():
40
+ dict_data[key] = pd.DataFrame(json_data.get(key)).T
41
+ #print("function - json_to_df: ", dict_data)
42
+ return dict_data
43
+
44
+ def generate_initial_population(population_size, num_gene):
45
+
46
+ """ generate initial population for Genetic Algorithm """
47
+
48
+ best_list, best_obj = [], []
49
+ population_list = []
50
+ makespan_record = []
51
+ for i in range(population_size):
52
+ nxm_random_num = list(np.random.permutation(num_gene)) # generate a random permutation of 0 to num_job*num_mc-1
53
+ population_list.append(nxm_random_num) # add to the population_list
54
+ for j in range(num_gene):
55
+ population_list[i][j] = population_list[i][j] % num_job # convert to job number format, every job appears m times
56
+ #print("function - generate_initial_population: ", population_list)
57
+
58
+ return population_list
59
+
60
+
61
+
62
+ def job_schedule(data_dict, population_size = 30, crossover_rate = 0.8, mutation_rate = 0.2, mutation_selection_rate = 0.2, num_iteration = 3000):
63
+
64
+ """ initialize genetic algorithm parameters and read data """
65
+ data_json = json_to_df(data_dict)
66
+ #print(data_json)
67
+ machine_sequence_tmp = data_json['Machines Sequence']
68
+ process_time_tmp = data_json['Processing Time']
69
+ # print("Machine_sequence_tmp: ",machine_sequence_tmp)
70
+ # print("Processing_time_tmp: ",process_time_tmp)
71
+
72
+ df_shape = process_time_tmp.shape
73
+ num_machines = df_shape[1] # number of machines
74
+ num_job = df_shape[0] # number of jobs
75
+ num_gene = num_machines * num_job # number of genes in a chromosome
76
+ num_mutation_jobs = round(num_gene * mutation_selection_rate)
77
+
78
+ process_time = [list(map(int, process_time_tmp.iloc[i])) for i in range(num_job)]
79
+ machine_sequence = [list(map(int, machine_sequence_tmp.iloc[i])) for i in range(num_job)]
80
+
81
+ #start_time = time.time()
82
+
83
+ Tbest = 999999999999999
84
+
85
+ best_list, best_obj = [], []
86
+ population_list = []
87
+ makespan_record = []
88
+
89
+ for i in range(population_size):
90
+ nxm_random_num = list(np.random.permutation(num_gene)) # generate a random permutation of 0 to num_job*num_mc-1
91
+ population_list.append(nxm_random_num) # add to the population_list
92
+ for j in range(num_gene):
93
+ population_list[i][j] = population_list[i][j] % num_job # convert to job number format, every job appears m times
94
+ #population_list = generate_initial_population(population_size=population_size, num_gene=num_gene)
95
+
96
+ for iteration in range(num_iteration):
97
+ Tbest_now = 99999999999
98
+
99
+ """ Two Point Cross-Over """
100
+ parent_list = copy.deepcopy(population_list)
101
+ offspring_list = copy.deepcopy(population_list) # generate a random sequence to select the parent chromosome to crossover
102
+ pop_random_size = list(np.random.permutation(population_size))
103
+
104
+ for size in range(int(population_size/2)):
105
+ crossover_prob = np.random.rand()
106
+ if crossover_rate >= crossover_prob:
107
+ parent_1 = population_list[pop_random_size[2*size]][:]
108
+ parent_2 = population_list[pop_random_size[2*size+1]][:]
109
+
110
+ child_1 = parent_1[:]
111
+ child_2 = parent_2[:]
112
+ cutpoint = list(np.random.choice(num_gene, 2, replace=False))
113
+ cutpoint.sort()
114
+
115
+ child_1[cutpoint[0]:cutpoint[1]] = parent_2[cutpoint[0]:cutpoint[1]]
116
+ child_2[cutpoint[0]:cutpoint[1]] = parent_1[cutpoint[0]:cutpoint[1]]
117
+ offspring_list[pop_random_size[2*size]] = child_1[:]
118
+ offspring_list[pop_random_size[2*size+1]] = child_2[:]
119
+
120
+
121
+ for pop in range(population_size):
122
+
123
+ """ Repairment """
124
+ job_count = {}
125
+ larger, less = [], [] # 'larger' record jobs appear in the chromosome more than pop times, and 'less' records less than pop times.
126
+ for job in range(num_job):
127
+ if job in offspring_list[pop]:
128
+ count = offspring_list[pop].count(job)
129
+ pos = offspring_list[pop].index(job)
130
+ job_count[job] = [count, pos] # store the above two values to the job_count dictionary
131
+ else:
132
+ count = 0
133
+ job_count[job] = [count, 0]
134
+
135
+ if count > num_machines:
136
+ larger.append(job)
137
+ elif count < num_machines:
138
+ less.append(job)
139
+
140
+ for large in range(len(larger)):
141
+ change_job = larger[large]
142
+ while job_count[change_job][0] > num_machines:
143
+ for les in range(len(less)):
144
+ if job_count[less[les]][0] < num_machines:
145
+ offspring_list[pop][job_count[change_job][1]] = less[les]
146
+ job_count[change_job][1] = offspring_list[pop].index(change_job)
147
+ job_count[change_job][0] = job_count[change_job][0]-1
148
+ job_count[less[les]][0] = job_count[less[les]][0]+1
149
+ if job_count[change_job][0] == num_machines:
150
+ break
151
+
152
+
153
+
154
+ for off_spring in range(len(offspring_list)):
155
+
156
+ """ Mutations """
157
+ mutation_prob = np.random.rand()
158
+ if mutation_rate >= mutation_prob:
159
+ m_change = list(np.random.choice(num_gene, num_mutation_jobs, replace=False)) # chooses the position to mutation
160
+ t_value_last = offspring_list[off_spring][m_change[0]] # save the value which is on the first mutation position
161
+ for i in range(num_mutation_jobs-1):
162
+ offspring_list[off_spring][m_change[i]] = offspring_list[off_spring][m_change[i+1]] # displacement
163
+ # move the value of the first mutation position to the last mutation position
164
+ offspring_list[off_spring][m_change[num_mutation_jobs-1]] = t_value_last
165
+
166
+
167
+
168
+ """ fitness value (calculate makespan) """
169
+ total_chromosome = copy.deepcopy(parent_list) + copy.deepcopy(offspring_list) # parent and offspring chromosomes combination
170
+ chrom_fitness, chrom_fit = [], []
171
+ total_fitness = 0
172
+ for pop_size in range(population_size*2):
173
+ j_keys = [j for j in range(num_job)]
174
+ key_count = {key:0 for key in j_keys}
175
+ j_count = {key:0 for key in j_keys}
176
+ m_keys = [j+1 for j in range(num_machines)]
177
+ m_count = {key:0 for key in m_keys}
178
+
179
+ for i in total_chromosome[pop_size]:
180
+ gen_t = int(process_time[i][key_count[i]])
181
+ gen_m = int(machine_sequence[i][key_count[i]])
182
+ j_count[i] = j_count[i] + gen_t
183
+ m_count[gen_m] = m_count[gen_m] + gen_t
184
+
185
+ if m_count[gen_m] < j_count[i]:
186
+ m_count[gen_m] = j_count[i]
187
+ elif m_count[gen_m] > j_count[i]:
188
+ j_count[i] = m_count[gen_m]
189
+
190
+ key_count[i] = key_count[i] + 1
191
+
192
+ makespan = max(j_count.values())
193
+ chrom_fitness.append(1/makespan)
194
+ chrom_fit.append(makespan)
195
+ total_fitness = total_fitness + chrom_fitness[pop_size]
196
+
197
+
198
+ """ Selection (roulette wheel approach) """
199
+ pk, qk = [], []
200
+
201
+ for size in range(population_size * 2):
202
+ pk.append(chrom_fitness[size] / total_fitness)
203
+ for size in range(population_size * 2):
204
+ cumulative = 0
205
+
206
+ for j in range(0, size+1):
207
+ cumulative = cumulative + pk[j]
208
+ qk.append(cumulative)
209
+
210
+ selection_rand = [np.random.rand() for i in range(population_size)]
211
+
212
+ for pop_size in range(population_size):
213
+ if selection_rand[pop_size] <= qk[0]:
214
+ population_list[pop_size] = copy.deepcopy(total_chromosome[0])
215
+ else:
216
+ for j in range(0, population_size * 2-1):
217
+ if selection_rand[pop_size] > qk[j] and selection_rand[pop_size] <= qk[j+1]:
218
+ population_list[pop_size] = copy.deepcopy(total_chromosome[j+1])
219
+ break
220
+
221
+
222
+ """ comparison """
223
+ for pop_size in range(population_size * 2):
224
+ if chrom_fit[pop_size] < Tbest_now:
225
+ Tbest_now = chrom_fit[pop_size]
226
+ sequence_now = copy.deepcopy(total_chromosome[pop_size])
227
+ if Tbest_now <= Tbest:
228
+ Tbest = Tbest_now
229
+ sequence_best = copy.deepcopy(sequence_now)
230
+
231
+ makespan_record.append(Tbest)
232
+
233
+ """ Results - Makespan """
234
+
235
+ print("optimal sequence", sequence_best)
236
+ print("optimal value:%f"%Tbest)
237
+ print("\n")
238
+ #print('the elapsed time:%s'% (time.time() - start_time))
239
+ op = "Optimal Schedule Length :"+str(Tbest)
240
+ #%matplotlib inline
241
+ plt.plot([i for i in range(len(makespan_record))],makespan_record,'b')
242
+ plt.ylabel('makespan', fontsize=15)
243
+ plt.xlabel('generation', fontsize=15)
244
+ plt.show()
245
+
246
+
247
+ """ plot gantt chart """
248
+
249
+ m_keys = [j+1 for j in range(num_machines)]
250
+ j_keys = [j for j in range(num_job)]
251
+ key_count = {key:0 for key in j_keys}
252
+ j_count = {key:0 for key in j_keys}
253
+ m_count = {key:0 for key in m_keys}
254
+ j_record = {}
255
+ for i in sequence_best:
256
+ gen_t = int(process_time[i][key_count[i]])
257
+ gen_m = int(machine_sequence[i][key_count[i]])
258
+ j_count[i] = j_count[i] + gen_t
259
+ m_count[gen_m] = m_count[gen_m] + gen_t
260
+
261
+ if m_count[gen_m] < j_count[i]:
262
+ m_count[gen_m] = j_count[i]
263
+ elif m_count[gen_m] > j_count[i]:
264
+ j_count[i] = m_count[gen_m]
265
+
266
+ start_time = str(datetime.timedelta(seconds = j_count[i] - process_time[i][key_count[i]])) # convert seconds to hours, minutes and seconds
267
+ end_time = str(datetime.timedelta(seconds = j_count[i]))
268
+
269
+ j_record[(i, gen_m)] = [start_time, end_time]
270
+
271
+ key_count[i] = key_count[i] + 1
272
+
273
+
274
+ df = []
275
+ for m in m_keys:
276
+ for j in j_keys:
277
+ df.append(dict(Task='Machine %s'%(m), Start='2020-02-01 %s'%(str(j_record[(j,m)][0])), \
278
+ Finish='2020-02-01 %s'%(str(j_record[(j,m)][1])),Resource='Job %s'%(j+1)))
279
+
280
+ df_data = pd.DataFrame(df)
281
+ df_ = pd.DataFrame(df)
282
+ df_.Start = pd.to_datetime(df_['Start'])
283
+ df_.Finish = pd.to_datetime(df_['Finish'])
284
+ start = df_.Start.min()
285
+ end = df_.Finish.max()
286
+
287
+ df_.Start = df_.Start.apply(lambda x: x.strftime('%Y-%m-%dT%H:%M:%S'))
288
+ df_.Finish = df_.Finish.apply(lambda x: x.strftime('%Y-%m-%dT%H:%M:%S'))
289
+ data = df_.to_dict('record')
290
+
291
+ final_data ={
292
+ 'start':start.strftime('%Y-%m-%dT%H:%M:%S'),
293
+ 'end':end.strftime('%Y-%m-%dT%H:%M:%S'),
294
+ 'data':data}
295
+
296
+ fig = ff.create_gantt(df, index_col='Resource', show_colorbar=True, group_tasks=True, showgrid_x=True, title='Job shop Schedule')
297
+
298
+ #iplot(fig, filename='GA_job_shop_scheduling')
299
+ return op, fig, df_data
MIP.py ADDED
@@ -0,0 +1,133 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # importing required modules
2
+ import pandas as pd
3
+ import numpy as np
4
+ import matplotlib.pyplot as plt
5
+ import pandas as pd
6
+ import chart_studio.plotly as py
7
+ import plotly.figure_factory as ff
8
+ import datetime
9
+ from datetime import timedelta
10
+ import time
11
+ import copy
12
+
13
+ #importing PuLP solver
14
+ import pulp
15
+ from itertools import product
16
+
17
+ def gantt_for_MIP(df,method):
18
+ title_chart = 'Job shop Schedule - using '+ method
19
+ fig = ff.create_gantt(df, index_col='Resource', show_colorbar=True, group_tasks=True, showgrid_x=True, title=title_chart)
20
+ return fig
21
+
22
+
23
+ def MIP_fn(input_file):
24
+ xls = pd.ExcelFile(input_file)
25
+ df1 = pd.read_excel(xls, 'Machines Sequence',index_col = 0)
26
+ df2 = pd.read_excel(xls, 'Processing Time',index_col = 0)
27
+
28
+ times =[]
29
+ machines = []
30
+
31
+
32
+ for ind in df1.index:
33
+ list_m = []
34
+ list_t = []
35
+ for col in df1:
36
+ list_m.append(df1[col][ind]-1) # for MIP formulation
37
+ list_t.append(df2[col][ind])
38
+ times.append(list_t)
39
+ machines.append(list_m)
40
+
41
+
42
+ pt = df2.values
43
+ mo = df1.values-1 # for the numbering the machines from 0 to 9
44
+
45
+ m=n=len(pt[0])
46
+ print("The number of machines and jobs in the given input data is - ",m)
47
+
48
+ #Max time possible for all the jobs - summation of all the process time
49
+ M = sum(times[i][j] for i in range(n) for j in range(m))
50
+
51
+ model = pulp.LpProblem("MIN_makespan", pulp.LpMinimize)
52
+
53
+ dv = pulp.LpVariable.dicts("start_time",
54
+ ((i, j) for i in range(m) for j in range(n)),
55
+ lowBound=0,
56
+ cat='Continuous')
57
+ #print(dv)
58
+ b = pulp.LpVariable.dicts("binary_var",
59
+ ((j, k, i) for i in range(m) for k in range(n) for j in range(n)),
60
+ lowBound=0,
61
+ cat='Binary')
62
+ # print(b)
63
+ Cmax = pulp.LpVariable('Cmax',lowBound = 0, cat='Continuous')
64
+
65
+
66
+ # (1) operation sequence inside a job
67
+ model += Cmax
68
+ for j in range(m):
69
+ for i in range(n-1):
70
+ I = mo[j,i]
71
+ K = mo[j,i+1]
72
+ J = j
73
+
74
+ model += (dv[J,K] - dv[J,I]) >= pt[J][list(mo[J]).index(I)]
75
+ # print((dv[J,K] - dv[J,I]) >= pt[J][list(mo[J]).index(I)])
76
+
77
+ # (2) the max start time
78
+ for j in range(m):
79
+ for i in range(n):
80
+
81
+ I = mo[j,i]
82
+ J = j
83
+ model += (Cmax - dv[J,I]) >= pt[J][list(mo[J]).index(I)]
84
+ #print((Cmax - dv[J,I]) >= pt[J][list(mo[J]).index(I)])
85
+
86
+ # (3) no overlap for operations assigned to same machine
87
+ for (j, k) in product(range(n), range(n)):
88
+ if k != j:
89
+ for i in range(m):
90
+ model += dv[j,i] - dv[k,i] + M*b[j,k,i] >= pt[k][list(mo[k]).index(i)]
91
+ model += -dv[j,i] + dv[k,i] - M*b[j,k,i] >= pt[j][list(mo[j]).index(i)] - M
92
+
93
+ start_time = time.time()
94
+ model.solve(pulp.PULP_CBC_CMD(timeLimit=200))
95
+ print("Solution status : ",pulp.LpStatus[model.status])
96
+
97
+ # for var in dv:
98
+ # var_value = dv[var].varValue
99
+ # print( var[0],'-',var[1] ,var_value)
100
+
101
+ tota_time = pulp.value(model.objective)
102
+ print ('Optimal time:',tota_time)
103
+ print('Solver processing time:%s'% (time.time() - start_time))
104
+ op = "Optimal Schedule Length :"+ str(tota_time)
105
+
106
+ m_keys=[j for j in range(m)]
107
+ j_keys=[j for j in range(n)]
108
+ key_count={key:0 for key in j_keys}
109
+ j_count={key:0 for key in j_keys}
110
+ m_count={key:0 for key in m_keys}
111
+ j_record={}
112
+
113
+ for var in dv:
114
+ var_value = dv[var].varValue
115
+
116
+ start_time=str(datetime.timedelta(seconds=var_value)) # convert seconds to hours, minutes and seconds
117
+ end_time=str(datetime.timedelta(seconds=var_value+pt[var[1]][list(mo[var[1]]).index(var[0])]))
118
+
119
+ j_record[(var[1],var[0])]=[start_time,end_time]
120
+
121
+ key_count[i]=key_count[i]+1
122
+
123
+
124
+ df=[]
125
+ for m in m_keys:
126
+ for j in j_keys:
127
+ df.append(dict(Task='Machine %s'%(m), Start='2020-02-01 %s'%(str(j_record[(j,m)][0])), Finish='2020-02-01 %s'%(str(j_record[(j,m)][1])),Resource='Job %s'%(j)))
128
+
129
+ method = 'PuLP - CBC Solver'
130
+ df_data = pd.DataFrame(df)
131
+
132
+ return op,gantt_for_MIP(df,method),df_data
133
+
OR_tools.py ADDED
@@ -0,0 +1,148 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # importing required modules
2
+ import pandas as pd
3
+ import numpy as np
4
+ import matplotlib.pyplot as plt
5
+ import pandas as pd
6
+ import chart_studio.plotly as py
7
+ import plotly.figure_factory as ff
8
+ import datetime
9
+ from datetime import timedelta
10
+ import time
11
+ import copy
12
+
13
+ #importing OR-tools solver
14
+ import collections
15
+ from ortools.sat.python import cp_model
16
+
17
+ def gantt_for_OR(df,method):
18
+ title_chart = 'Job shop Schedule - using '+ method
19
+ fig = ff.create_gantt(df, index_col='Resource', show_colorbar=True, group_tasks=True, showgrid_x=True, title=title_chart)
20
+ return fig
21
+
22
+ def or_fn(input_file):
23
+ xls = pd.ExcelFile(input_file)
24
+ df1 = pd.read_excel(xls, 'Machines Sequence',index_col = 0)
25
+ df2 = pd.read_excel(xls, 'Processing Time',index_col = 0)
26
+
27
+ jobs_data =[]
28
+
29
+ for ind in df1.index:
30
+ lists = []
31
+ for col in df1:
32
+ lists.append((df1[col][ind],df2[col][ind]))
33
+ jobs_data.append(lists)
34
+
35
+ model = cp_model.CpModel()
36
+ machines_count = 1 + max(task[0] for job in jobs_data for task in job)
37
+ all_machines = range(machines_count)
38
+ # Computes horizon dynamically as the sum of all durations.
39
+ horizon = sum(task[1] for job in jobs_data for task in job)
40
+
41
+ # Named tuple to store information about created variables.
42
+ task_type = collections.namedtuple('task_type', 'start end interval')
43
+ # Named tuple to manipulate solution information.
44
+ assigned_task_type = collections.namedtuple('assigned_task_type',
45
+ 'start job index duration')
46
+
47
+ # Creates job intervals and add to the corresponding machine lists.
48
+ all_tasks = {}
49
+ machine_to_intervals = collections.defaultdict(list)
50
+
51
+ for job_id, job in enumerate(jobs_data):
52
+ for task_id, task in enumerate(job):
53
+ machine = task[0]
54
+ duration = task[1]
55
+ suffix = '_%i_%i' % (job_id, task_id)
56
+ start_var = model.NewIntVar(0, horizon, 'start' + suffix)
57
+ end_var = model.NewIntVar(0, horizon, 'end' + suffix)
58
+ interval_var = model.NewIntervalVar(start_var, duration, end_var,
59
+ 'interval' + suffix)
60
+ all_tasks[job_id, task_id] = task_type(start=start_var,
61
+ end=end_var,
62
+ interval=interval_var)
63
+ machine_to_intervals[machine].append(interval_var)
64
+
65
+ # Create and add disjunctive constraints.
66
+ for machine in all_machines:
67
+ model.AddNoOverlap(machine_to_intervals[machine])
68
+
69
+ # Precedences inside a job.
70
+ for job_id, job in enumerate(jobs_data):
71
+ for task_id in range(len(job) - 1):
72
+ model.Add(all_tasks[job_id, task_id +
73
+ 1].start >= all_tasks[job_id, task_id].end)
74
+
75
+ # Makespan objective.
76
+ obj_var = model.NewIntVar(0, horizon, 'makespan')
77
+ model.AddMaxEquality(obj_var, [
78
+ all_tasks[job_id, len(job) - 1].end
79
+ for job_id, job in enumerate(jobs_data)
80
+ ])
81
+ model.Minimize(obj_var)
82
+
83
+ start_time = time.time()
84
+
85
+ solver = cp_model.CpSolver()
86
+ status = solver.Solve(model)
87
+
88
+ print("--- %s seconds ---" % (time.time() - start_time))
89
+
90
+ if status == cp_model.OPTIMAL or status == cp_model.FEASIBLE:
91
+ print('Solution:')
92
+ # Create one list of assigned tasks per machine.
93
+ assigned_jobs = collections.defaultdict(list)
94
+ for job_id, job in enumerate(jobs_data):
95
+ for task_id, task in enumerate(job):
96
+ machine = task[0]
97
+ assigned_jobs[machine].append(
98
+ assigned_task_type(start=solver.Value(
99
+ all_tasks[job_id, task_id].start),
100
+ job=job_id,
101
+ index=task_id,
102
+ duration=task[1]))
103
+
104
+ # Create per machine output lines.
105
+ output = ''
106
+ for machine in all_machines:
107
+ # Sort by starting time.
108
+ assigned_jobs[machine].sort()
109
+ sol_line_tasks = 'Machine ' + str(machine) + ': '
110
+ sol_line = ' '
111
+
112
+ for assigned_task in assigned_jobs[machine]:
113
+ name = 'job_%i_task_%i' % (assigned_task.job,
114
+ assigned_task.index)
115
+ # Add spaces to output to align columns.
116
+ sol_line_tasks += '%-15s' % name
117
+
118
+ start = assigned_task.start
119
+ duration = assigned_task.duration
120
+ sol_tmp = '[%i,%i]' % (start, start + duration)
121
+ # Add spaces to output to align columns.
122
+ sol_line += '%-15s' % sol_tmp
123
+
124
+ sol_line += '\n'
125
+ sol_line_tasks += '\n'
126
+ output += sol_line_tasks
127
+ output += sol_line
128
+
129
+ # Finally print the solution found.
130
+ print(f'Optimal Schedule Length: {solver.ObjectiveValue()}')
131
+ op = "Optimal Schedule Length : "+ str(solver.ObjectiveValue())
132
+ #print(output)
133
+ else:
134
+ print('No solution found.')
135
+ op = "No solution found."
136
+ #Solution df:
137
+ df = []
138
+ for machine in all_machines:
139
+ for task in assigned_jobs[machine]:
140
+ df.append(dict(Task='Machine %s'%(machine), Start='2020-02-01 %s'%(str(timedelta(seconds=int(task[0])))), \
141
+ Finish='2020-02-01 %s'%(str(timedelta(seconds=int(task[0]+task[3])))),Resource='Job %s'%(task[1])))
142
+
143
+ method = 'OR Tools'
144
+ fig = gantt_for_OR(df,method)
145
+ fig.show()
146
+ df_data = pd.DataFrame(df)
147
+
148
+ return op,gantt_for_OR(df,method),df_data
README.md ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: OptimizationJSSP
3
+ emoji: 🌍
4
+ colorFrom: pink
5
+ colorTo: pink
6
+ sdk: gradio
7
+ sdk_version: 3.29.0
8
+ app_file: app.py
9
+ pinned: false
10
+ license: mit
11
+ duplicated_from: GowriHari/OptimizationJSSP
12
+ ---
13
+
14
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
app.py ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # importing required modules
2
+ import pandas as pd
3
+ import numpy as np
4
+ import matplotlib.pyplot as plt
5
+ import datetime
6
+ from datetime import timedelta
7
+ import time
8
+ import copy
9
+ import openpyxl as xl
10
+ import json
11
+ import gradio as gr
12
+ import io
13
+ from PIL import Image
14
+
15
+ #importing 3 functions:
16
+ import GA
17
+ import OR_tools as ort
18
+ import MIP
19
+
20
+ def plotly_fig2array(fig):
21
+ #convert Plotly fig to an array
22
+ fig_bytes = fig.to_image(format="png")
23
+ buf = io.BytesIO(fig_bytes)
24
+ img = Image.open(buf)
25
+ return np.asarray(img)
26
+
27
+ def JSSP(method,temp_file):
28
+ xls = pd.ExcelFile(temp_file.name)
29
+ print(xls)
30
+ print("Input File is :",xls)
31
+ if method == "Genetic Algo":
32
+ op, fig, df = GA.input_file(xls)
33
+
34
+ elif method == "Python OR":
35
+ op, fig, df = ort.or_fn(xls)
36
+
37
+ else:
38
+ op, fig, df = MIP.MIP_fn(xls)
39
+
40
+ img = plotly_fig2array(fig)
41
+ # data = pd.DataFrame(df)
42
+ df.to_excel("op_data.xlsx",index=False)
43
+ data_op = ["op_data.xlsx"]
44
+ return op, img,data_op
45
+
46
+ demo = gr.Interface(JSSP,
47
+ inputs=[gr.Radio(["Genetic Algo", "Python OR", "MIP solution"], label="Solution Method", info="Choose one method to solve ypur Job Shop problem."),gr.File(label = 'Input File')],
48
+ examples =[["Python OR","Demo_data.xlsx"]],
49
+ outputs = ["text","image",gr.File(label='Output File')],
50
+ title = "Job Shop Scheduling Problem",
51
+ description = "Upload an excel file containing Machine Sequnce and Processing Time for a n x m problem to optimize makespan for manufacturing or Job schedule. Choose the method you want the problem to be solved before clicking Submit! \n For Demo purpose, use the example data provided BELOW.",
52
+ cache_examples = False )
53
+
54
+
55
+ demo.queue().launch(debug=True,show_error=True)
requirements.txt ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ chart-studio==1.1.0
2
+ convertdate==2.4.0
3
+ gradio==3.32.0
4
+ gradio_client==0.2.5
5
+ jsonpickle==3.0.1
6
+ jsonschema==4.3.3
7
+ matplotlib==3.7.1
8
+ matplotlib-inline==0.1.6
9
+ matplotlib-venn==0.11.9
10
+ more-itertools==9.1.0
11
+ numpy==1.22.4
12
+ openpyxl==3.0.10
13
+ orjson==3.8.12
14
+ ortools==9.6.2534
15
+ pandas==1.5.3
16
+ pandas-datareader==0.10.0
17
+ pandas-gbq==0.17.9
18
+ plotly==5.13.1
19
+ PuLP==2.7.0
20
+ python-dateutil==2.8.2
21
+ python-utils==3.5.2
22
+ scikit-image==0.19.3
23
+ scikit-learn==1.2.2
24
+ scipy==1.10.1
25
+ seaborn==0.12.2
26
+ sklearn-pandas==2.2.0
27
+ kaleido
28
+ pillow