def mutation_insert(flowshop, population, mutation_probability=0.4): """ Returns a new population after mutation given an instance of the flowshop permutation problem, a population and a mutation probability (insert method) :param flowshop: a Flowshop object :param population: list of Ordonnancement objects :param mutation_probability: probability for each Ordonnancement object in the population to mutate :return: the population after mutation """ nb_jobs = flowshop.nombre_jobs() mutated_population = [] for sched in population: if random.random() < mutation_probability: sequence = sched.sequence().copy() indices = [i for i in range(nb_jobs)] elt_index, insert_index = random.sample(indices, 2) temp = sequence[elt_index] sequence.remove(temp) sequence.insert(insert_index, temp) mutated_sched = Ordonnancement(sched.nb_machines) mutated_sched.ordonnancer_liste_job(sequence) mutated_population.append(mutated_sched) else: mutated_population.append(sched) return mutated_population
def crossover_position(sched1, sched2): """ crossing rules : two lists of jobs' positions : [2,3,5,1,4] & [3,4,1,2,5] the index of the lists are the jobs and the values are the positions. We sum the two lists : [5,7,6,3,9] and we rearenge it [2,4,3,1,5] :param sched1: the first parent :param sched2: the second parent :return: the new Ordonnancenement object """ seq1 = sched1.sequence() seq2 = sched2.sequence() positionschild = [0] * len(seq1) for i in range(len(seq1)): job = seq1[i] positionschild[i] = seq1.index(job) + seq2.index(job) childseq = [] for i in range(len(positionschild)): min_position = min(positionschild) indices_next_job = [] for j in range(len(positionschild)): if positionschild[j] == min_position: indices_next_job.append(j) index_next_job = random.sample(indices_next_job, 1)[0] next_job = seq1[index_next_job] childseq.append(next_job) positionschild[index_next_job] = 2 * len(seq1) + 1 nb_machines = sched1.nb_machines childsched = Ordonnancement(nb_machines) childsched.ordonnancer_liste_job(childseq) return [childsched]
def crossover_1_point(sched1, sched2, point1): """ Crosses two schedulings with the :param sched1: parent 1 for crossover (Ordonnancement object) :param sched2: parent 2 for crossover (Ordonnancement object) :param point1: separation point to swap the sub-sequences, INTEGER between 0 and nb_jobs :return population: the two children schedulings (Ordonnancement objects) """ seq1 = sched1.sequence().copy() seq2 = sched2.sequence().copy() seq11 = seq1[0:point1] seq12 = seq1[point1:] seq21 = seq2[0:point1] seq22 = seq2[point1:] list_exclude = [[], []] for i in range(len(seq11)): if seq11[i] not in seq21: list_exclude[0].append(i) if seq21[i] not in seq11: list_exclude[1].append(i) for j in range(len(list_exclude[0])): k = random.randint(0, len(list_exclude[1]) - 1) n = list_exclude[1][k] m = list_exclude[0][j] seq11[m], seq21[n] = seq21[n], seq11[m] list_exclude[1].pop(k) new_seq1 = seq11 + seq22 new_seq2 = seq21 + seq12 nb_machines = sched1.nb_machines scheduling1 = Ordonnancement(nb_machines) scheduling2 = Ordonnancement(nb_machines) scheduling1.ordonnancer_liste_job(new_seq1) scheduling2.ordonnancer_liste_job(new_seq2) return [scheduling1, scheduling2]
def test_improvement_with_ls(self): job_a = Job(0, [1, 5]) job_b = Job(1, [5, 1]) flow_shop_2 = Flowshop(2, 2, [job_a, job_b]) swap_neighbors = create_swap_neighbors(flow_shop_2) insert_neighbors = create_insert_neighbors(flow_shop_2) scheduling = Ordonnancement(job_a.nb_op) scheduling.ordonnancer_liste_job([job_b, job_a]) new_scheduling_swap = local_search_swap(scheduling, 1, max_neighbors_nb=50, neighbors=swap_neighbors) new_scheduling_insert = local_search_insert(scheduling, 1, max_neighbors_nb=50, neighbors=insert_neighbors) self.assertTrue(scheduling.duree() == 11) self.assertTrue(new_scheduling_swap.duree() < scheduling.duree()) self.assertTrue(new_scheduling_insert.duree() < scheduling.duree()) self.assertTrue(new_scheduling_swap.duree() == 7) self.assertTrue(new_scheduling_insert.duree() == 7) self.assertEqual(len(new_scheduling_swap.sequence()), 2) self.assertEqual(len(new_scheduling_insert.sequence()), 2) for job in [job_a, job_b]: self.assertIn(job, new_scheduling_swap.sequence()) self.assertIn(job, new_scheduling_insert.sequence())
def test_crossover_positions(self): parent_1 = Ordonnancement(job_1.nb_op) parent_2 = Ordonnancement(job_1.nb_op) parent_1.ordonnancer_liste_job([job_2, job_3, job_4, job_5, job_1]) parent_2.ordonnancer_liste_job([job_1, job_4, job_5, job_2, job_3]) new_pop = crossover_position(parent_1, parent_2) for sched in new_pop: self.assertEqual(len(sched.sequence()), 5) self.assertEqual(sched.has_duplicate(), False) for job in [job_1, job_2, job_3, job_4, job_5]: self.assertIn(job, sched.sequence())
def random_initial_pop(flow_shop, rdm_size): """ Generates randomly the initial population :param flow_shop: an instance of the flow shop permutation problem :param rdm_size: number of element in the initial population to generate :return: the random part of the initial population for the memetic algorithm """ population_seq = [] population = [] start = [flow_shop.liste_jobs(i) for i in range(flow_shop.nb_jobs)] for i in range(rdm_size): random.shuffle(start) elem = copy.copy(start) population_seq.append(elem) for seq in population_seq: temp_scheduling = Ordonnancement(flow_shop.nb_machines) temp_scheduling.ordonnancer_liste_job(seq) population.append(temp_scheduling) return population
def test_crossover_2_points(self): parent_1 = Ordonnancement(job_1.nb_op) parent_2 = Ordonnancement(job_1.nb_op) parent_1.ordonnancer_liste_job([job_2, job_3, job_4, job_5, job_1]) parent_2.ordonnancer_liste_job([job_1, job_4, job_5, job_2, job_3]) initial_pop = [parent_1, parent_2] new_pop = crossover_2_points(parent_1, parent_2, 1, 3) self.assertEqual(len(initial_pop), len(new_pop)) for sched in new_pop: self.assertEqual(len(sched.sequence()), 5) self.assertEqual(sched.has_duplicate(), False) for job in [job_1, job_2, job_3, job_4, job_5]: self.assertIn(job, sched.sequence())
def local_search_insert(scheduling, iteration, max_neighbors_nb, neighbors): """ Returns a (new) scheduling after local search on the given initial scheduling during the maximum number of iterations given to find a minimum local, given an instance of the flowshop population (insert neighborhood) An iteration is an exploration of all the insert neighborhood of the current best scheduling :param scheduling: a scheduling (Ordonnancement object) :param iteration: maximum number of iterations (explorations of the insert neighborhood) to find a minimum local :param max_neighbors_nb: number of neighbors to visit at each iteration :param neighbors: list of neighbors :return: the scheduling after the given number of iteration of local search """ best_scheduling = copy.copy(scheduling) candidate = copy.copy(scheduling) duration = best_scheduling.duree() duration_candidate = candidate.duree() if max_neighbors_nb > len(neighbors): max_neighbors_nb = len(neighbors) for a in range(0, iteration): visited_neighbors = neighbors.copy() for k in range(max_neighbors_nb): index = random.randint(0, len(visited_neighbors) - 1) i, j = visited_neighbors[index][0], visited_neighbors[index][1] visited_neighbors.pop(index) temp = copy.copy(best_scheduling) sequence = temp.sequence().copy() ls_insert = sequence[i] sequence.remove(ls_insert) sequence.insert(j, ls_insert) new_scheduling = Ordonnancement(temp.nb_machines) new_scheduling.ordonnancer_liste_job(sequence) duration_temp = new_scheduling.duree() if duration_temp < duration_candidate: duration_candidate = duration_temp candidate = new_scheduling if duration > duration_candidate: best_scheduling = candidate duration = duration_candidate else: break return best_scheduling
def mutation_swap(flowshop, population, mutation_probability=0.4): """ Returns a new population after mutation given an instance of the flowshop permutation problem, a population and a mutation probability (swap method) :param flowshop: a Flowshop object :param population: list of Ordonnancement objects :param mutation_probability: probability for each Ordonnancement object in the population to mutate :return: the population after mutation """ nb_jobs = flowshop.nombre_jobs() mutated_population = [] for sched in population: if random.random() < mutation_probability: sequence = sched.sequence().copy() indices = [j for j in range(nb_jobs)] a, b = random.sample(indices, 2) sequence[a], sequence[b] = sequence[b], sequence[a] mutated_sched = Ordonnancement(sched.nb_machines) mutated_sched.ordonnancer_liste_job(sequence) mutated_population.append(mutated_sched) else: mutated_population.append(sched) return mutated_population
def test_crossover(self): parent_1 = Ordonnancement(job_1.nb_op) parent_2 = Ordonnancement(job_1.nb_op) parent_1.ordonnancer_liste_job([job_2, job_3, job_4, job_5, job_1]) parent_2.ordonnancer_liste_job([job_1, job_4, job_5, job_2, job_3]) initial_pop = [parent_1, parent_2] flowshop = Flowshop(5, 5) new_pop = crossover(flowshop, initial_pop, cross_1_point_prob=0.5, cross_2_points_prob=0.5, cross_position_prob=0.3, gentrification=True) self.assertEqual(len(initial_pop), len(new_pop)) for sched in new_pop: self.assertEqual(len(sched.sequence()), 5) self.assertEqual(sched.has_duplicate(), False) for job in [job_1, job_2, job_3, job_4, job_5]: self.assertIn(job, sched.sequence())
def deterministic_initial_pop(flow_shop, deter_size, best_deter): """ Generates deterministically the initial population :param flow_shop: an instance of the flow shop permutation problem :param deter_size: number of element in the initial population to generate :param best_deter: if true, select the initial population by scheduling duration, else random selection :return: the deterministic part of the initial population for the memetic algorithm """ all_deterministic_seq = [] neh_seq = neh_order(flow_shop) all_deterministic_seq.append(neh_seq) for m_index in range(flow_shop.nb_machines): temp_sep_desc = job_duration_order_desc(flow_shop, m_index) temp_sep_asc = job_duration_order_asc(flow_shop, m_index) all_deterministic_seq.append(temp_sep_asc) all_deterministic_seq.append(temp_sep_desc) for sum_index in range(flow_shop.nb_machines - 1): temp_johnson_seq = johnson_rule_order(flow_shop, sum_index) all_deterministic_seq.append(temp_johnson_seq) deter_pop = [] if best_deter: all_deterministic_ordo = [] for seq in all_deterministic_seq: sched = Ordonnancement(flow_shop.nb_machines) sched.ordonnancer_liste_job(seq) all_deterministic_ordo.append(sched) sorted_scheduling = sorted(all_deterministic_ordo, key=lambda o: o.duree(), reverse=False) if deter_size > len(sorted_scheduling): deter_pop = sorted_scheduling else: deter_pop = sorted_scheduling[0:deter_size] else: if deter_size > len(all_deterministic_seq): seq_deter_sample = all_deterministic_seq else: seq_deter_sample = random.sample(all_deterministic_seq, deter_size) for seq in seq_deter_sample: sched = Ordonnancement(flow_shop.nb_machines) sched.ordonnancer_liste_job(seq) deter_pop.append(sched) return deter_pop
def crossover_2_points(sched1, sched2, point1, point2): """ Crosses two schedulings with the :param sched1: parent 1 for crossover (Ordonnancement object) :param sched2: parent 2 for crossover (Ordonnancement object) :param point1: first point of the interval to swap, INTEGER between 0 and nb_jobs :param point2: second point of the interval to swap, INTEGER between 0 and nb_jobs, different of point1 :return population: the two children schedulings (Ordonnancement objects) """ nb_jobs = len(sched1.sequence()) point1, point2 = min(point1, point2), max(point1, point2) seq1 = sched1.sequence().copy() seq2 = sched2.sequence().copy() seq11 = seq1[0:point1] seq12 = seq1[point1:point2] seq13 = seq1[point2:nb_jobs] seq21 = seq2[0:point1] seq22 = seq2[point1:point2] seq23 = seq2[point2:nb_jobs] list_exclude = [[], []] for i in range(len(seq12)): if seq12[i] not in seq22: list_exclude[0].append(i) if seq22[i] not in seq12: list_exclude[1].append(i) for j in range(len(list_exclude[0])): k = random.randint(0, len(list_exclude[1]) - 1) n = list_exclude[1][k] m = list_exclude[0][j] seq12[m], seq22[n] = seq22[n], seq12[m] list_exclude[1].pop(k) new_seq1 = seq11 + seq22 + seq13 new_seq2 = seq21 + seq12 + seq23 nb_machines = sched1.nb_machines scheduling1 = Ordonnancement(nb_machines) scheduling2 = Ordonnancement(nb_machines) scheduling1.ordonnancer_liste_job(new_seq1) scheduling2.ordonnancer_liste_job(new_seq2) return [scheduling1, scheduling2]
def neh_order(flow_shop): """ Compute the NEH scheduling of flow_shop :param flow_shop: an instance of the flow shop permutation problem :return: return the NEH scheduling of flow_shop """ sorted_jobs = sorted(flow_shop.l_job, key=lambda j: j.duree(), reverse=True) best_order = [] for job in sorted_jobs: min_duration = MAXINT best_pos = 0 for i in range(0, len(best_order) + 1): scheduling = Ordonnancement(flow_shop.nb_machines) new_list = [j for j in best_order] new_list.insert(i, job) scheduling.ordonnancer_liste_job(new_list) if scheduling.duree() < min_duration: min_duration = scheduling.duree() best_pos = i best_order.insert(best_pos, job) return best_order
def swap(i, j, scheduling): sequence = scheduling.sequence().copy() sequence[i], sequence[j] = sequence[j], sequence[i] new_scheduling = Ordonnancement(scheduling.nb_machines) new_scheduling.ordonnancer_liste_job(sequence) return new_scheduling
import unittest import copy from src.job import Job from src.ordonnancement import Ordonnancement from src.flowshop import Flowshop from src.local_search import local_search_swap, local_search_insert, swap, local_search, create_swap_neighbors, \ create_insert_neighbors job_1 = Job(1, [1, 1, 1, 1, 10]) job_2 = Job(2, [1, 1, 1, 4, 8]) job_3 = Job(3, [2, 1, 3, 5, 1]) job_4 = Job(4, [2, 5, 5, 3, 3]) job_5 = Job(5, [1, 1, 3, 7, 1]) flow_shop = Flowshop(5, 5) scheduling_1 = Ordonnancement(job_1.nb_op) scheduling_2 = Ordonnancement(job_2.nb_op) scheduling_1.ordonnancer_liste_job([job_2, job_3, job_4, job_5, job_1]) scheduling_2.ordonnancer_liste_job([job_1, job_4, job_5, job_2, job_3]) initial_scheduling = Ordonnancement(job_1.nb_op) initial_scheduling.ordonnancer_liste_job([job_1, job_2, job_3, job_4, job_5]) class TestSolutionLocalSearchClassMethods(unittest.TestCase): def test_local_search(self): initial_pop = [scheduling_1, scheduling_2, initial_scheduling] swap_neighbors = create_swap_neighbors(flow_shop) insert_neighbors = create_insert_neighbors(flow_shop) new_pop = local_search(initial_pop, local_search_swap_prob=0.5, local_search_insert_prob=0.5, maximum_nb_iterations=20,
return sorted_jobs def custom_formatwarning(msg, *args, **kwargs): # ignore everything except the message return str(msg) + '\n' # Executable part to prompt the NEH results for each instance if __name__ == "__main__": import os from src.flowshop import Flowshop from src.utils import read_global_memetic_results, get_best_known_and_found_solutions data_path = "../data/" global_memetic_results_path = "../res/global_memetic_results.csv" for dataSet in os.listdir(data_path): for instance in os.listdir(data_path + dataSet): flow_shop_file_path = data_path + dataSet + "/" + instance flow_shop_instance = Flowshop() flow_shop_instance.definir_par(flow_shop_file_path) file_name = instance.split('.txt')[0] global_memetic_results = read_global_memetic_results(global_memetic_results_path) best_known, best_found = get_best_known_and_found_solutions(global_memetic_results, file_name) neh_scheduling = Ordonnancement(flow_shop_instance.nombre_machines()) neh_scheduling.ordonnancer_liste_job(neh_order(flow_shop_instance)) neh_c_max = neh_scheduling.duree() relative_gap = round(((neh_c_max - best_known) / best_known) * 100, 2) print("Solving instance " + data_path + dataSet + '/' + file_name + ' with NEH... ' + str(neh_c_max) + ' ' + str(relative_gap) + '%') print("Finished!")
def test_neh_order(self): seq = ip.neh_order(flowshop_2) sched = Ordonnancement(flowshop_2.nb_machines) sched.ordonnancer_liste_job(seq) self.assertEqual(sched.duree(), 705)
import unittest from src.ordonnancement import Ordonnancement from src.job import Job job_1 = Job(1, [1, 1, 1, 1, 10]) job_2 = Job(2, [1, 1, 1, 4, 8]) job_3 = Job(3, [2, 1, 3, 5, 1]) job_4 = Job(4, [2, 5, 5, 3, 3]) job_5 = Job(5, [1, 1, 3, 7, 1]) ord_1 = Ordonnancement(job_1.nb_op) ord_2 = Ordonnancement(job_1.nb_op) ord_3 = Ordonnancement(job_1.nb_op) ord_1.ordonnancer_liste_job([job_2, job_3, job_4, job_5, job_1]) ord_2.ordonnancer_liste_job([job_1, job_4, job_5, job_2, job_3]) ord_3.ordonnancer_liste_job([job_2, job_3, job_4, job_5, job_1]) class TestOrdonnancementClassMethods(unittest.TestCase): def test_eq(self): self.assertEqual(ord_1, ord_3) self.assertNotEqual(ord_1, ord_2) if __name__ == '__main__': unittest.main()
:param ordonnancement: an Ordonnancement object where the scheduling of all jobs is done (in other words which represents a solution to an instance of the flow-shop permutation problem) :param file_path: path where the html file corresponding to the representation of the solution is stored (it needs to have the character "/" at the end or be the empty string) :param file_name: name of the html file corresponding to the representation of the solution :param show_durations: boolean which indicates if the duration of the tasks have to be represented (True by default) """ figure, figure_name = create_solution_figure(ordonnancement, show_durations) if not file_name == "": figure_name = file_name figure.write_html(file_path + figure_name + '.html') return None # "main" to give an example of how to use the "visualisation.py" methods if __name__ == "__main__": a = Job(1, [1, 1, 1, 1, 10]) b = Job(2, [1, 1, 1, 4, 8]) c = Job(3, [2, 1, 3, 5, 1]) d = Job(4, [2, 5, 5, 3, 3]) e = Job(5, [1, 1, 3, 7, 1]) scheduling = Ordonnancement(5) scheduling.ordonnancer_job(a) scheduling.ordonnancer_job(b) scheduling.ordonnancer_job(c) scheduling.ordonnancer_job(d) scheduling.ordonnancer_job(e) # show_solution_figure(scheduling) save_solution_as_html(scheduling)
import unittest from src.mutation import mutation, mutation_insert, mutation_swap from src.flowshop import Flowshop from src.job import Job from src.ordonnancement import Ordonnancement flow_shop = Flowshop(5, 5) job_1 = Job(1, [1, 1, 1, 1, 10]) job_2 = Job(2, [1, 1, 1, 4, 8]) job_3 = Job(3, [2, 1, 3, 5, 1]) job_4 = Job(4, [2, 5, 5, 3, 3]) job_5 = Job(5, [1, 1, 3, 7, 1]) scheduling_1 = Ordonnancement(job_1.nb_op) scheduling_2 = Ordonnancement(job_1.nb_op) scheduling_3 = Ordonnancement(job_1.nb_op) scheduling_1.ordonnancer_liste_job([job_2, job_3, job_4, job_5, job_1]) scheduling_2.ordonnancer_liste_job([job_1, job_4, job_5, job_2, job_3]) scheduling_3.ordonnancer_liste_job([job_5, job_4, job_3, job_2, job_1]) initial_pop = [scheduling_1, scheduling_2, scheduling_3] class TestMutationFileMethods(unittest.TestCase): def test_mutation_swap(self): new_pop = mutation_swap(flow_shop, initial_pop, mutation_probability=1.0) self.assertEqual(len(initial_pop), len(new_pop)) for scheduling in new_pop: self.assertEqual(len(scheduling.sequence()), 5) self.assertEqual(scheduling.has_duplicate(), False) for job in [job_1, job_2, job_3, job_4, job_5]: