def simulated_hardening(matrix, data, free, filled, groups_empty_space, teachers_empty_space, subjects_order, file): """ Algorithm that uses simulated hardening with geometric decrease of temperature to optimize timetable by satisfying soft constraints as much as possible (empty space for groups and existence of an hour in which there is no classes). """ # number of iterations iter_count = 2500 # temperature t = 0.5 _, _, curr_cost_group = empty_space_groups_cost(groups_empty_space) _, _, curr_cost_teachers = empty_space_teachers_cost(teachers_empty_space) curr_cost = curr_cost_group # + curr_cost_teachers if free_hour(matrix) == -1: curr_cost += 1 for i in range(iter_count): rt = random.uniform(0, 1) t *= 0.99 # geometric decrease of temperature # save current results old_matrix = copy.deepcopy(matrix) old_free = copy.deepcopy(free) old_filled = copy.deepcopy(filled) old_groups_empty_space = copy.deepcopy(groups_empty_space) old_teachers_empty_space = copy.deepcopy(teachers_empty_space) old_subjects_order = copy.deepcopy(subjects_order) # try to mutate 1/4 of all classes for j in range(len(data.classes) // 4): index_class = random.randrange(len(data.classes)) mutate_ideal_spot(matrix, data, index_class, free, filled, groups_empty_space, teachers_empty_space, subjects_order) _, _, new_cost_groups = empty_space_groups_cost(groups_empty_space) _, _, new_cost_teachers = empty_space_teachers_cost(teachers_empty_space) new_cost = new_cost_groups # + new_cost_teachers if free_hour(matrix) == -1: new_cost += 1 if new_cost < curr_cost or rt <= math.exp((curr_cost - new_cost) / t): # take new cost and continue with new data curr_cost = new_cost else: # return to previously saved data matrix = copy.deepcopy(old_matrix) free = copy.deepcopy(old_free) filled = copy.deepcopy(old_filled) groups_empty_space = copy.deepcopy(old_groups_empty_space) teachers_empty_space = copy.deepcopy(old_teachers_empty_space) subjects_order = copy.deepcopy(old_subjects_order) if i % 100 == 0: print('Iteration: {:4d} | Average cost: {:0.8f}'.format(i, curr_cost)) print('TIMETABLE AFTER HARDENING') show_timetable(matrix) print('STATISTICS AFTER HARDENING') show_statistics(matrix, data, subjects_order, groups_empty_space, teachers_empty_space) write_solution_to_file(matrix, data, filled, file, groups_empty_space, teachers_empty_space, subjects_order)
def show_statistics(matrix, data, subjects_order, groups_empty_space, teachers_empty_space): """ Prints statistics. """ cost_hard = check_hard_constraints(matrix, data) if cost_hard == 0: print('Hard constraints satisfied: 100.00 %') else: print('Hard constraints NOT satisfied, cost: {}'.format(cost_hard)) print('Soft constraints satisfied: {:.02f} %\n'.format( subjects_order_cost(subjects_order))) empty_groups, max_empty_group, average_empty_groups = empty_space_groups_cost( groups_empty_space) print('TOTAL empty space for all GROUPS and all days: ', empty_groups) print('MAX empty space for GROUP in day: ', max_empty_group) print('AVERAGE empty space for GROUPS per week: {:.02f}\n'.format( average_empty_groups)) empty_teachers, max_empty_teacher, average_empty_teachers = empty_space_teachers_cost( teachers_empty_space) print('TOTAL empty space for all TEACHERS and all days: ', empty_teachers) print('MAX empty space for TEACHER in day: ', max_empty_teacher) print('AVERAGE empty space for TEACHERS per week: {:.02f}\n'.format( average_empty_teachers))
def write_solution_to_file(matrix, data, filled, filepath, groups_empty_space, teachers_empty_space, subjects_order): """ Writes statistics and schedule to file. """ f = open('solution_files/sol_' + filepath, 'w') f.write( '-------------------------- STATISTICS --------------------------\n') cost_hard = check_hard_constraints(matrix, data) if cost_hard == 0: f.write('\nHard constraints satisfied: 100.00 %\n') else: f.write('Hard constraints NOT satisfied, cost: {}\n'.format(cost_hard)) f.write('Soft constraints satisfied: {:.02f} %\n\n'.format( subjects_order_cost(subjects_order))) empty_groups, max_empty_group, average_empty_groups = empty_space_groups_cost( groups_empty_space) f.write('TOTAL empty space for all GROUPS and all days: {}\n'.format( empty_groups)) f.write('MAX empty space for GROUP in day: {}\n'.format(max_empty_group)) f.write('AVERAGE empty space for GROUPS per week: {:.02f}\n\n'.format( average_empty_groups)) empty_teachers, max_empty_teacher, average_empty_teachers = empty_space_teachers_cost( teachers_empty_space) f.write('TOTAL empty space for all TEACHERS and all days: {}\n'.format( empty_teachers)) f.write( 'MAX empty space for TEACHER in day: {}\n'.format(max_empty_teacher)) f.write('AVERAGE empty space for TEACHERS per week: {:.02f}\n\n'.format( average_empty_teachers)) # f_hour = free_hour(matrix) # if f_hour != -1: # f.write('Free term -> {}\n'.format(f_hour)) # else: # f.write('NO hours without classes.\n') groups_dict = {} for group_name, group_index in data.groups.items(): if group_index not in groups_dict: groups_dict[group_index] = group_name days = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday'] hours = [7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18] f.write( '\n--------------------------- SCHEDULE ---------------------------') for class_index, times in filled.items(): c = data.classes[class_index] groups = ' ' for g in c.groups: groups += groups_dict[g] + ', ' f.write('\n\nClass {}\n'.format(class_index)) f.write( 'Teacher: {} \nSubject: {} \nGroups:{} \nType: {} \nDuration: {} hour(s)' .format(c.teacher, c.subject, groups[:len(groups) - 2], c.type, c.duration)) room = str(data.classrooms[times[0][1]]) f.write('\nClassroom: {:2s}\nTime: {}'.format(room[:room.rfind('-')], days[times[0][0] // 12])) for time in times: f.write(' {}'.format(hours[time[0] % 12])) f.close()
def simulated_hardening_utils(matrix, data, free, filled, groups_empty_space, teachers_empty_space, subjects_order, result, index): # create new data for parallel matrix_utils = copy.deepcopy(matrix) data_utils = copy.deepcopy(data) free_utils = copy.deepcopy(free) filled_utils = copy.deepcopy(filled) groups_empty_space_utils = copy.deepcopy(groups_empty_space) teachers_empty_space_utils = copy.deepcopy(teachers_empty_space) subjects_order_utils = copy.deepcopy(subjects_order) # number of iterations iter_count = 90 # temperature t = 0.5 - index * 0.05 _, _, curr_cost_group = empty_space_groups_cost(groups_empty_space) _, _, curr_cost_teachers = empty_space_teachers_cost(teachers_empty_space) curr_cost = curr_cost_group + curr_cost_teachers # if free_hour(matrix) == -1: # curr_cost += 1 for i in range(iter_count): rt = random.uniform(0, 1) t *= 0.99 # save current results old_matrix = copy.deepcopy(matrix_utils) old_free = copy.deepcopy(free_utils) old_filled = copy.deepcopy(filled_utils) old_groups_empty_space = copy.deepcopy(groups_empty_space_utils) old_teachers_empty_space = copy.deepcopy(teachers_empty_space_utils) old_subjects_order = copy.deepcopy(subjects_order_utils) # try to mutate 1/4 of all classes for j in range(len(data.classes) // 4): index_class = random.randrange(len(data_utils.classes)) mutate_ideal_spot(matrix_utils, data_utils, index_class, free_utils, filled_utils, groups_empty_space_utils, teachers_empty_space_utils, subjects_order_utils) _, _, new_cost_groups = empty_space_groups_cost( groups_empty_space_utils) _, _, new_cost_teachers = empty_space_teachers_cost( teachers_empty_space_utils) new_cost = new_cost_groups + new_cost_teachers # if free_hour(matrix) == -1: # new_cost += 1 if new_cost < curr_cost or rt <= math.exp((curr_cost - new_cost) / t): # take new cost and continue with new data curr_cost = new_cost else: # return to previously saved data matrix_utils = copy.deepcopy(old_matrix) free_utils = copy.deepcopy(old_free) filled_utils = copy.deepcopy(old_filled) groups_empty_space_utils = copy.deepcopy(old_groups_empty_space) teachers_empty_space_utils = copy.deepcopy( old_teachers_empty_space) subjects_order_utils = copy.deepcopy(old_subjects_order) if i % 10 == 0: print('Iteration: {:4d} | Average cost: {:0.8f}'.format( i, curr_cost)) result[index] = [ curr_cost, matrix_utils, free_utils, filled_utils, groups_empty_space_utils, teachers_empty_space_utils, subjects_order_utils ]