# -*- coding: utf-8 -*- """ Created on Sat Feb 15 22:08:35 2025 @author: engne """ import time import numpy as np import matplotlib.pyplot as plt import matplotlib.lines as mlines import pandas as pd import EnergyConsumptionCalculation # Parameters num_jobs = 10 num_operations_per_job = 7 total_operations = num_jobs * num_operations_per_job num_machines = 21 num_wolves = 300 num_iterations = 100 # Example Machine options and corresponding processing times for each operation in each job machine_options = np.array([ [1, 8, 15], # Operation 1 machines [2, 9, 16], # Operation 2 machines [3, 10, 17], # Operation 3 machines [4, 11, 18], # Operation 4 machines [5, 12, 19], # Operation 5 machines [6, 13, 20], # Operation 6 machines [7, 14, 21] # Operation 7 machines ]) processing_times = np.array([ [18, 6, 4], # Operation 1 times [24, 18, 12],# Operation 2 times [8, 6, 4], # Operation 3 times [12, 4, 3], # Operation 4 times [6, 2, 2], # Operation 5 times [2, 1, 1], # Operation 6 times [2, 2, 1] # Operation 7 times ]) # Initialize wolves (each wolf is a schedule) def initialize_wolves(num_wolves, num_jobs, num_operations_per_job): wolves = [] for _ in range(num_wolves): schedule = [] for job in range(1,num_jobs+1): for operation in range(1,num_operations_per_job+1): machine_choice = np.random.choice(3) # Randomly choose a machine option machine = machine_options[operation-1][machine_choice] time = processing_times[operation-1][machine_choice] start_time, end_time = 0, 0 schedule.append((job, operation, machine, time, start_time, end_time)) wolves.append(schedule) return wolves # Initialize wolves (each wolf is a schedule) def initialize_wolves2(num_wolves, num_jobs, num_operations_per_job,new_machine_options, new_processing_times): wolves = [] for _ in range(num_wolves): schedule = [] for job in range(1,num_jobs+1): for operation in range(1,num_operations_per_job+1): num_choices = len(new_machine_options[operation-1]) # Get the number of available machine choices machine_choice = np.random.choice(num_choices) # Randomly choose a machine option machine = new_machine_options[operation-1][machine_choice] time = new_processing_times[operation-1][machine_choice] start_time, end_time = 0, 0 schedule.append((job, operation, machine, time, start_time, end_time)) wolves.append(schedule) return wolves def Calculate_EC(finished_operations,executing_operations,best_schedule2,best_schedule): M_RT=[] M_RT_healthy=[] for job, operation, machine, start_time, end_time in finished_operations: if machine in [1,4,5,8,11,12,15,18,19]: time=end_time-start_time M_RT.append((machine,time)) for job, operation, machine, start_time, end_time in executing_operations: if machine in [1,4,5,8,11,12,15,18,19]: time=end_time-start_time M_RT.append((machine,time)) for job, operation, machine, processing_time, start_time, end_time in best_schedule2: if machine in [1,4,5,8,11,12,15,18,19]: time=end_time-start_time M_RT.append((machine,time)) for job, operation, machine, processing_time, start_time, end_time in best_schedule: if machine in [1,4,5,8,11,12,15,18,19]: time=end_time-start_time M_RT_healthy.append((machine,time)) print('healthy:') EnergyConsumptionCalculation.calculate_GWO_EC(M_RT_healthy) print('Scenario 1:') EnergyConsumptionCalculation.calculate_GWO_EC(M_RT) # Fitness function: Calculate makespan with precedence constraints def calculate_makespan(schedule): machine_avail_time = np.zeros(num_machines) # 21 machines, indexed 0-20 job_avail_time = np.zeros(num_jobs) modified_schedule = [] for job, operation, machine, time, start_time, end_time in schedule: # Respect precedence: start the current operation only after the previous one in the job is finished if operation > 0: # Ensure operation 0 (the first in job) starts as soon as possible prev_op_end_time = job_avail_time[job-1] else: prev_op_end_time = 0 start_time = max(machine_avail_time[machine-1], prev_op_end_time) end_time = start_time + time machine_avail_time[machine-1] = end_time job_avail_time[job-1] = end_time # Append the operation details including start and end times modified_schedule.append((job, operation, machine, time, start_time, end_time)) makespan = job_avail_time.max() return makespan, modified_schedule def calculate_makespan2(schedule, finished_operations, executing_operations, time_unit): machine_avail_time = np.full(num_machines, time_unit) # Default availability starts at failure time job_avail_time = np.zeros(num_jobs) # Preserve availability times for machines in finished and executing operations for op in finished_operations + executing_operations: job = op[0] machine = op[2] end_time = op[4] # End time when the operation finishes or is executed job_avail_time[job-1] = max(job_avail_time[job-1], end_time) machine_avail_time[machine-1] = max(machine_avail_time[machine-1], end_time) modified_schedule = [] for job, operation, machine, time, start_time, end_time in schedule: # Respect precedence: start the current operation only after the previous one in the job is finished prev_op_end_time = job_avail_time[job-1] if operation > 0 else 0 start_time = max(machine_avail_time[machine-1], prev_op_end_time) end_time = start_time + time machine_avail_time[machine-1] = end_time job_avail_time[job-1] = end_time modified_schedule.append((job, operation, machine, time, start_time, end_time)) makespan = job_avail_time.max() return makespan, modified_schedule # Function to filter remaining operations def filter_remaining_operations(schedule, finished_operations, executing_operations): excluded_operations = {(op[0], op[1]) for op in finished_operations + executing_operations} return [op for op in schedule if (op[0], op[1]) not in excluded_operations] # Function to get finished and executing operations def get_finished_and_executing_operations(schedule, time_unit, failed_machines): finished_operations = [] executing_operations = [] disrupted_operations=[] for job, operation, machine, status, start_time, end_time in schedule: if end_time <= time_unit: # Finished operations finished_operations.append((job+1, operation+1, machine, start_time, end_time)) elif start_time < time_unit < end_time and machine not in failed_machines: # Executing operations executing_operations.append((job+1, operation+1, machine, start_time, end_time)) if start_time < time_unit < end_time and machine in failed_machines: disrupted_operations.append((job+1, operation+1, machine, start_time, 'disrupted')) # print("finished_operations", finished_operations) # print("executing_operations", executing_operations) return finished_operations, executing_operations, disrupted_operations def filter_machine_options(machine_options, processing_times, failed_machines): new_machine_options = [] new_processing_times = [] for machines, times in zip(machine_options, processing_times): filtered_machines = [] filtered_times = [] for machine, time in zip(machines, times): if machine not in failed_machines: filtered_machines.append(machine) filtered_times.append(time) new_machine_options.append(filtered_machines) new_processing_times.append(filtered_times) return new_machine_options, new_processing_times # Function to plot the fitness evaluation over iterations def plot_fitness_evaluation(makespan_history): plt.figure(figsize=(10, 6)) plt.plot(range(1, len(makespan_history) + 1), makespan_history, linestyle='-', color='b') plt.xlabel('Iteration') plt.ylabel('Makespan') plt.title('Fitness Function Evaluation') plt.grid(True) plt.savefig('GWO_Fitness_Function.pdf', format='pdf', dpi=1200, bbox_inches='tight') plt.show() def plot_fitness_evaluation2(makespan_history,makespan_history2,x,best_makespan2): plt.plot(x, makespan_history, label = "Before Machine Failure") plt.plot(x, makespan_history2, label = "After Machine Failure") plt.xticks(range(0,110,10)) # plt.yticks(range(66,int(best_makespan2+6),2)) plt.xlabel('Iterations') plt.ylabel('Makespan') plt.autoscale() plt.grid() plt.legend(loc='upper right', bbox_to_anchor=(1, 1), shadow=True, ncol=1) plt.savefig('Scenario1.pdf', format='pdf', dpi=1200, bbox_inches='tight') plt.show() # Gantt Chart Function def draw_gantt_chart(schedule, makespan, num_machines): # Fixed colors for each job (up to 10 jobs, you can expand it as needed) fixed_colors = [ 'red', 'blue', 'green', 'orange', 'purple', 'brown', '#C71585', 'gray', 'teal', '#B8860B' ] plt.figure(figsize=(12, 8)) # Create a color map for jobs, using the fixed colors job_colors = {} for job in range(num_jobs): job_colors[job] = fixed_colors[job % len(fixed_colors)] # Loop through colors if more than 10 jobs for machine in range(1, num_machines + 1): operations = [op for op in schedule if op[2] == machine] for job, operation, _, _, start_time, end_time in operations: plt.barh(f'M{machine}', end_time - start_time, left=start_time, color=job_colors[job]) # Add the operation number inside the bar plt.text(start_time + (end_time - start_time) / 2, f'M{machine}', f'{job+1}-{operation+1}', va='center', ha='center', color='white', fontsize=8) plt.xticks(range(0, 90, 10)) plt.xlabel('Time') plt.ylabel('Machines') plt.grid(True) plt.legend(handles=[plt.Line2D([0], [0], color=job_colors[j], lw=4) for j in range(num_jobs)], labels=[f'Job {j+1}' for j in range(num_jobs)], bbox_to_anchor=(1.05, 1), loc='upper left') plt.tight_layout() plt.savefig('GWO_GANTT_Chart.pdf', format='pdf', dpi=1200, bbox_inches='tight') plt.show() def draw_gantt_chart_after_reschedule(best_schedule2, best_makespan2, num_machines, failed_machines, time_unit,finished_operations, executing_operations): # Fixed colors for each job (up to 10 jobs, you can expand it as needed) fixed_colors = [ 'red', 'blue', 'green', 'orange', 'purple', 'brown', '#C71585', 'gray', 'teal', '#B8860B' ] plt.figure(figsize=(12, 8)) # Create a color map for jobs, using the fixed colors job_colors = {} for job in range(num_jobs): job_colors[job] = fixed_colors[job % len(fixed_colors)] # Loop through colors if more than 10 jobs # Plot operations for each machine for machine in range(1, num_machines + 1): operations = [op for op in best_schedule2 if op[2] == machine] for job, operation, _, _, start_time, end_time in operations: # Draw operation bars for normal jobs plt.barh(f'M{machine}', end_time - start_time, left=start_time, color=job_colors[job-1]) # Add the operation number inside the bar plt.text(start_time + (end_time - start_time) / 2, f'M{machine}', f'{job}-{operation}', va='center', ha='center', color='white', fontsize=10) operations = [op for op in finished_operations if op[2] == machine] for job, operation, _, start_time, end_time in operations: # Draw operation bars for normal jobs plt.barh(f'M{machine}', end_time - start_time, left=start_time, color=job_colors[job-1]) # Add the operation number inside the bar plt.text(start_time + (end_time - start_time) / 2, f'M{machine}', f'{job}-{operation}', va='center', ha='center', color='white', fontsize=10) operations = [op for op in executing_operations if op[2] == machine] for job, operation, _, start_time, end_time in operations: # Draw operation bars for normal jobs plt.barh(f'M{machine}', end_time - start_time, left=start_time, color=job_colors[job-1]) # Add the operation number inside the bar plt.text(start_time + (end_time - start_time) / 2, f'M{machine}', f'{job}-{operation}', va='center', ha='center', color='white', fontsize=10) # If the machine is broken, draw a continuous black bar starting from the breakdown time if machine in failed_machines: plt.barh(f'M{machine}', best_makespan2 - time_unit, left=time_unit, color='black', height=0.8) # Continuous black bar after breakdown # Add a label for the broken machine (optional) plt.text(time_unit + (best_makespan2 - time_unit) / 2, f'M{machine}', f'failed', va='center', ha='center', color='white', fontsize=12) plt.xticks(range(0, int(best_makespan2) + 10, 10)) plt.xlabel('Time') plt.ylabel('Machines') plt.title(f'Gantt Chart - Makespan: {best_makespan2}') plt.grid(True) plt.legend(handles=[plt.Line2D([0], [0], color=job_colors[j], lw=4) for j in range(num_jobs)], labels=[f'Job {j+1}' for j in range(num_jobs)], bbox_to_anchor=(1.05, 1), loc='upper left') plt.tight_layout() plt.savefig('GWO_GANTT_Chart_After_Reschedule.pdf', format='pdf', dpi=1200, bbox_inches='tight') plt.show() #Scenario 2 def get_finished_and_executing_operations2(schedule, time_unit, failed_machines): finished_operations = [] executing_operations = [] disrupted_operations=[] for job, operation, machine, processing_time, start_time, end_time in schedule: if end_time <= time_unit: # Finished operations finished_operations.append((job, operation, processing_time, machine, start_time, end_time)) elif start_time < time_unit < end_time and machine in failed_machines: disrupted_operations.append((job, operation, machine, processing_time, start_time, 'disrupted')) elif start_time < time_unit < end_time and machine not in failed_machines: # Executing operations executing_operations.append((job, operation, machine, processing_time, start_time, end_time)) elif start_time >= time_unit and machine in failed_machines: disrupted_operations.append((job, operation, machine, processing_time, start_time, 'disrupted')) finished_operations.sort(key=lambda x: (x[0], x[1])) disrupted_operations.sort(key=lambda x: (x[0], x[1])) executing_operations.sort(key=lambda x: (x[0], x[1])) return finished_operations, executing_operations, disrupted_operations def filter_machine_options2(machine_options, processing_times, failed_machines): new_machine_options = [] new_processing_times = [] for machines, times in zip(machine_options, processing_times): filtered_machines = [] filtered_times = [] for machine, time in zip(machines, times): if machine not in failed_machines: filtered_machines.append(machine) filtered_times.append(time) new_machine_options.append(filtered_machines) new_processing_times.append(filtered_times) return new_machine_options, new_processing_times def get_following_operations(disrupted_operations, best_schedule): following_operations = [] disrupted_jobs = [(op[0], op[1]) for op in disrupted_operations] # List of (job_id, disrupted_op_id) for job, operation, machine, processing_time, start_time, end_time in best_schedule: for disrupted_job, disrupted_op in disrupted_jobs: if job == disrupted_job and operation > disrupted_op: if (job, operation) not in [(job[0], job[1]) for job in disrupted_operations]: following_operations.append((job, operation, machine, processing_time, start_time, end_time)) following_operations2 = list(set(following_operations)) following_operations2.sort(key=lambda x: (x[0], x[1])) return following_operations2 def get_machine_availability(best_schedule, num_machines): """ Computes and stores the availability time of each machine after the initial schedule. Parameters: - schedule: List of tuples (job_id, op_id, machine, start_time, end_time) - num_machines: Total number of machines Returns: - List where index represents the machine ID and value represents the machine's availability time. """ # Initialize the machine availability list with zeros machine_availability = [0] * num_machines for job_id, op_id, machine, processing_time , start_time, end_time in best_schedule: machine_availability[machine-1] = max(machine_availability[machine-1], end_time) return machine_availability # Function to redistribute operations of multiple failed machines def redistribute_failed_operations(following_operations,machine_availability,best_schedule, finished_operations, executing_operations, disrupted_operations, failed_machines, new_machine_options, new_processing_times, time_unit): """ Redistributes operations from failed machines to their alternative machines while preserving the schedule structure. Parameters: - best_schedule: List of operations as tuples (job_id, op_id, machine, start_time, end_time). - finished_operations: List of completed operations. - executing_operations: List of ongoing operations. - disrupted_operations: List of operations affected by machine failures. - failed_machines: Set of machines that have failed. - new_machine_options: Dictionary mapping failed machines to their alternative machines. - new_processing_times: List of tuples (job_id, op_id, machine, processing_time). - time_unit: The current time unit at which redistribution is taking place. Returns: - Updated schedule with redistributed operations and adjusted timings, and makespan. """ import copy new_schedule = copy.deepcopy(best_schedule) print(len(new_schedule)) # Remove disrupted operations and following operations from schedule new_schedule = [op for op in new_schedule if (op[0], op[1]) not in [(job[0], job[1]) for job in disrupted_operations]] print(len(new_schedule)) new_schedule = [op for op in new_schedule if (op[0], op[1]) not in [(job[0], job[1]) for job in following_operations]] print(len(new_schedule)) print(new_schedule) # Step 1: Redistribute operations to alternative machines for op in disrupted_operations: job_id, op_id, failed_machine, processing_time, start_time, end_time = op # alt_machines = new_machine_options[failed_machine] num_choices = len(new_machine_options[op_id]) # print(new_machine_options[op_id-1],len(new_machine_options[op_id-1])) best_machine = None best_start_time = float("inf") best_end_time = float("inf") i=0 for machine in new_machine_options[op_id]: # print(machine) # Compute start time considering precedence constraints new_schedule.sort(key=lambda x: (x[0], x[1])) prev_op = next((o for o in new_schedule if o[0] == job_id and o[1] == op_id - 1), None) # print(prev_op) if prev_op: new_start_time = max(prev_op[5], time_unit,machine_availability[machine-1]) else: new_start_time = max(time_unit,machine_availability[machine-1]) # Find processing time for this operation on the current alternative machine processing_time = None processing_time = new_processing_times[op_id][i] if processing_time is None: processing_time = end_time - start_time # Fallback to the original time # Compute end time new_end_time = new_start_time + processing_time # Choose the machine with the earliest end time if new_end_time < best_end_time: best_end_time = new_end_time best_start_time = new_start_time best_machine = machine # machine_availability[best_machine - 1] = best_end_time i+=1 machine_availability[best_machine - 1] = best_end_time # print(machine_availability) # Insert updated operation into the schedule new_schedule.append((job_id, op_id, best_machine, processing_time ,best_start_time, best_end_time)) new_schedule.sort(key=lambda x: (x[0], x[1])) following_operations.sort(key=lambda x: (x[0], x[1])) for op in following_operations: if not any(op[0] == d[0] and op[1] == d[1] for d in disrupted_operations): job_id2, op_id, machine, processing_time, start_time, end_time = op if job_id2==job_id: prev_op = next((o for o in new_schedule if o[0] == job_id2 and o[1] == op_id - 1), None) # print(prev_op) if prev_op: start_time = max(prev_op[5], machine_availability[machine-1]) end_time = start_time + processing_time machine_availability[machine - 1] = end_time new_schedule.append((job_id2, op_id, machine, processing_time ,start_time, end_time)) new_schedule.sort(key=lambda x: (x[0], x[1])) # Step 2: Sort schedule by job and operation order new_schedule.sort(key=lambda x: (x[0], x[1])) print(len(new_schedule)) print(new_schedule) # Step 3: Calculate the makespan makespan = max(op[5] for op in new_schedule) # The makespan is the maximum end time # print(makespan) return new_schedule, makespan def draw_gantt_chart2(schedule, makespan, num_machines,failed_machines,time_unit): # Fixed colors for each job (up to 10 jobs, you can expand it as needed) fixed_colors = [ 'red', 'blue', 'green', 'orange', 'purple', 'brown', '#C71585', 'gray', 'teal', '#B8860B' ] plt.figure(figsize=(12, 8)) # Create a color map for jobs, using the fixed colors job_colors = {} for job in range(num_jobs): job_colors[job] = fixed_colors[job % len(fixed_colors)] # Loop through colors if more than 10 jobs for machine in range(1, num_machines + 1): operations = [op for op in schedule if op[2] == machine] for job, operation, _, _, start_time, end_time in operations: plt.barh(f'M{machine}', end_time - start_time, left=start_time, color=job_colors[job]) # Add the operation number inside the bar plt.text(start_time + (end_time - start_time) / 2, f'M{machine}', f'{job+1}-{operation+1}', va='center', ha='center', color='white', fontsize=8) if machine in failed_machines: plt.barh(f'M{machine}', makespan - time_unit, left=time_unit, color='black', height=0.8) # Continuous black bar after breakdown # Add a label for the broken machine (optional) plt.text(time_unit + (makespan - time_unit) / 2, f'M{machine}', f'failed', va='center', ha='center', color='white', fontsize=12) plt.xticks(range(0, int(makespan+10), 10)) plt.xlabel('Makespan') plt.ylabel('Machines') plt.grid(True) plt.legend(handles=[plt.Line2D([0], [0], color=job_colors[j], lw=4) for j in range(num_jobs)], labels=[f'Job {j+1}' for j in range(num_jobs)], bbox_to_anchor=(1.05, 1), loc='upper left') plt.tight_layout() plt.savefig('GWO_GANTT_Chart.pdf', format='pdf', dpi=1200, bbox_inches='tight') plt.show() def Calculate_EC2(new_schedule_Scenario2): M_RT=[] for job, operation, machine, processing_time, start_time, end_time in new_schedule_Scenario2: if machine in [1,4,5,8,11,12,15,18,19]: time=end_time-start_time M_RT.append((machine,time)) print('Scenario 2:') EnergyConsumptionCalculation.calculate_GWO_EC(M_RT) # Main GWO loop with tracking of minimum makespan start = time.time() wolves = initialize_wolves(num_wolves, num_jobs, num_operations_per_job) # print(wolves[299]) alpha, beta, delta = None, None, None # To track the best makespan across all iterations best_makespan = float('inf') best_schedule = None makespan_history = [] # To store makespan at each iteration for iteration in range(num_iterations): # Evaluate fitness of all wolves fitness_values, modified_schedules = zip(*[calculate_makespan(wolf) for wolf in wolves]) # Sort wolves by fitness (lower is better) sorted_wolves = sorted(zip(fitness_values, modified_schedules, wolves)) alpha = sorted_wolves[0][1] if alpha is None or sorted_wolves[0][0] < calculate_makespan(alpha)[0] else alpha beta = sorted_wolves[1][1] if beta is None or sorted_wolves[1][0] < calculate_makespan(beta)[0] else beta delta = sorted_wolves[2][1] if delta is None or sorted_wolves[2][0] < calculate_makespan(delta)[0] else delta # Update positions of wolves for i in range(num_wolves): for job in range(num_jobs): for op_idx in range(num_operations_per_job): # Calculate A and C coefficients for the three best solutions A1, A2, A3 = 2 * np.random.random() - 1, 2 * np.random.random() - 1, 2 * np.random.random() - 1 C1, C2, C3 = 2 * np.random.random(), 2 * np.random.random(), 2 * np.random.random() # print(C1,C2,C3) # Retrieve the machine assignments from alpha, beta, and delta wolves alpha_pos = alpha[job*num_operations_per_job + op_idx][2] beta_pos = beta[job*num_operations_per_job + op_idx][2] delta_pos = delta[job*num_operations_per_job + op_idx][2] wolf_pos = wolves[i][job*num_operations_per_job + op_idx][2] # Calculate the new position D_alpha = abs(C1 * alpha_pos - wolf_pos) D_beta = abs(C2 * beta_pos - wolf_pos) D_delta = abs(C3 * delta_pos - wolf_pos) new_pos = (alpha_pos - A1 * D_alpha + beta_pos - A2 * D_beta + delta_pos - A3 * D_delta) / 3 # Clamp to available machine options closest_machine_idx = np.argmin(abs(machine_options[op_idx] - new_pos)) wolves[i][job*num_operations_per_job + op_idx] = ( job, op_idx, machine_options[op_idx][closest_machine_idx], processing_times[op_idx][closest_machine_idx], 0, # Placeholder for start time 0 # Placeholder for end time ) # Calculate the current iteration's best makespan and modified schedule current_best_makespan, current_best_schedule = calculate_makespan(alpha) # Update the best makespan if current is better if current_best_makespan < best_makespan: best_makespan = current_best_makespan best_schedule = current_best_schedule # Store the best makespan for this iteration makespan_history.append(current_best_makespan) print(f"Iteration {iteration + 1}/{num_iterations}, Current Best Makespan: {current_best_makespan}, Overall Best Makespan: {best_makespan}") # Final Output print("Final best schedule:", best_schedule) print("Final best makespan:", best_makespan) # Draw Gantt Chart for the best schedule # plot_fitness_evaluation(makespan_history) if best_schedule is not None: draw_gantt_chart(best_schedule, best_makespan, num_machines) # Reschedule in case of machine failure time_unit = 10 # Example time unit when machines break down failed_machines = [19] # Example broken machines finished_operations, executing_operations, disrupted_operations=get_finished_and_executing_operations(best_schedule, time_unit,failed_machines) print("finished operations are: ",finished_operations) print("excuting operations are: ", executing_operations) print("disrupted operations are: ", disrupted_operations) new_machine_options, new_processing_times=filter_machine_options(machine_options, processing_times, failed_machines) print(new_machine_options) print(new_processing_times) number_removed_operations=len(finished_operations+executing_operations) wolves = initialize_wolves2(num_wolves, num_jobs, num_operations_per_job,new_machine_options, new_processing_times) # print(wolves[299]) # Remove finished and executing operations from the dataset filtered_wolves = [filter_remaining_operations(wolf, finished_operations, executing_operations) for wolf in wolves] alpha, beta, delta = None, None, None # To track the best makespan across all iterations best_makespan2 = float('inf') best_schedule2 = None x=[] makespan_history2 = [] # To store makespan at each iteration for iteration in range(num_iterations): # Evaluate fitness of all wolves fitness_values, modified_schedules = zip(*[calculate_makespan2(wolf,finished_operations, executing_operations,time_unit) for wolf in filtered_wolves]) # Sort wolves by fitness (lower is better) sorted_wolves = sorted(zip(fitness_values, modified_schedules, filtered_wolves)) alpha = sorted_wolves[0][1] if alpha is None or sorted_wolves[0][0] < calculate_makespan(alpha)[0] else alpha beta = sorted_wolves[1][1] if beta is None or sorted_wolves[1][0] < calculate_makespan(beta)[0] else beta delta = sorted_wolves[2][1] if delta is None or sorted_wolves[2][0] < calculate_makespan(delta)[0] else delta # Update positions of wolves for i in range(num_wolves): for job in range(num_jobs): for op_idx in range(num_operations_per_job): # Calculate A and C coefficients for the three best solutions A1, A2, A3 = 2 * np.random.random() - 1, 2 * np.random.random() - 1, 2 * np.random.random() - 1 C1, C2, C3 = 2 * np.random.random(), 2 * np.random.random(), 2 * np.random.random() # print(C1,C2,C3) # Retrieve the machine assignments from alpha, beta, and delta wolves alpha_pos = alpha[job*num_operations_per_job + op_idx-number_removed_operations][2] beta_pos = beta[job*num_operations_per_job + op_idx-number_removed_operations][2] delta_pos = delta[job*num_operations_per_job + op_idx-number_removed_operations][2] wolf_pos = filtered_wolves[i][job*num_operations_per_job + op_idx-number_removed_operations][2] # Calculate the new position D_alpha = abs(C1 * alpha_pos - wolf_pos) D_beta = abs(C2 * beta_pos - wolf_pos) D_delta = abs(C3 * delta_pos - wolf_pos) new_pos = (alpha_pos - A1 * D_alpha + beta_pos - A2 * D_beta + delta_pos - A3 * D_delta) / 3 # Clamp to available machine options closest_machine_idx = np.argmin(abs(new_machine_options[op_idx] - new_pos)) filtered_wolves[i][job*num_operations_per_job + op_idx-number_removed_operations] = ( job, op_idx, new_machine_options[op_idx][closest_machine_idx], new_processing_times[op_idx][closest_machine_idx], 0, # Placeholder for start time 0 # Placeholder for end time ) # After updating positions, calculate the makespan and track the best schedule current_best_makespan2, current_best_schedule2 = calculate_makespan2(alpha,finished_operations, executing_operations,time_unit) # Update the best makespan if current is better if current_best_makespan2 < best_makespan2: best_makespan2 = current_best_makespan2 best_schedule2 = current_best_schedule2 x.append(iteration) makespan_history2.append(current_best_makespan2) print(f"Iteration {iteration + 1}/{num_iterations}, Current Best Makespan2: {current_best_makespan2}, Overall Best Makespan: {best_makespan2}") # Final Output print("Final best schedule2:", best_schedule2) print("Final best makespan2:", best_makespan2) # Draw Gantt Chart for the best schedule # plot_fitness_evaluation(makespan_history2) draw_gantt_chart_after_reschedule(best_schedule2, best_makespan2, num_machines, failed_machines, time_unit,finished_operations, executing_operations) plot_fitness_evaluation2(makespan_history,makespan_history2,x,best_makespan2) finished_operations2, executing_operations2, disrupted_operations2=get_finished_and_executing_operations2(best_schedule, time_unit,failed_machines) new_machine_options2, new_processing_times2=filter_machine_options2(machine_options, processing_times, failed_machines) following_operations=get_following_operations(disrupted_operations2, best_schedule) machine_availability=get_machine_availability(best_schedule, num_machines) new_schedule_Scenario2, new_makespan_Scenario2 = redistribute_failed_operations(following_operations,machine_availability,best_schedule, finished_operations2, executing_operations2, disrupted_operations2, failed_machines, new_machine_options2, new_processing_times2, time_unit) draw_gantt_chart2(new_schedule_Scenario2, new_makespan_Scenario2, num_machines,failed_machines,time_unit) print('new makespan= ',new_makespan_Scenario2) Calculate_EC(finished_operations,executing_operations,best_schedule2,best_schedule) Calculate_EC2(new_schedule_Scenario2)