def position_swap(self, tabu_list):
        position_1, position_2 = neighborhood.get_random_positions(
            self.timetable)

        # check if the moves are already in the tabu list
        # if not, add them to the list
        if (position_1, position_2) in tabu_list or (position_2,
                                                     position_1) in tabu_list:
            return False
        tabu_list.append((position_1, position_2))
        tabu_list.append((position_2, position_1))

        # make a back up, so a rollback is possible
        timetable_back_up = copy.deepcopy(self.timetable)
        events_back_up = copy.deepcopy(self.events)
        success, self.timetable, self.events = neighborhood.swap_positions(
            self.timetable,
            self.events,
            position_1,
            position_2,
            feasibility=False)

        if not success:
            self.timetable = timetable_back_up
            self.events = events_back_up
            return False
        # shuffle events, and try to place them in a random order
        random.shuffle(self.events)
        events_to_remove = []
        for event in self.events:
            for position in self.timetable.empty_positions:
                room_fi_number = position[0]
                time_slot = position[1]
                room = gi.class_rooms_dict[room_fi_number]
                if hc.course_event_fits_into_time_slot(
                        event, time_slot + self.timetable.offset *
                        40) and hc.room_capacity_constraint(event, room):
                    self.timetable.assign_course_to_position(event, position)
                    events_to_remove.append(event)
                    break

        # removed assigned events
        for event in events_to_remove:
            self.events.remove(event)

        distance = len(self.events)
        delta_e = distance - self.last_distance

        if delta_e > 0:
            self.timetable = timetable_back_up
            self.events = events_back_up
            return False
        # Success!
        self.last_distance = distance
        if self.last_distance < self.best_distance:
            self.best_feasible_tt = copy.deepcopy(self.timetable)
            self.best_distance = distance
        return True
    def occupied_unplaced_time_slot_swap(self, tabu_list):
        time_slot = neighborhood.get_random_time_slot()
        if time_slot in tabu_list:
            return False
        tabu_list.append(time_slot)

        timetable_back_up = copy.deepcopy(self.timetable)
        events_back_up = copy.deepcopy(self.events)

        success, self.timetable, self.events = neighborhood.swap_occupied_for_unplaced_in_time_slot(
            self.timetable, self.events, time_slot)

        if not success:
            self.timetable = timetable_back_up
            self.events = events_back_up
            return False

        # shuffle events, and try to place them in a random order
        random.shuffle(self.events)
        events_to_remove = []
        for event in self.events:
            for position in self.timetable.empty_positions:
                room_fi_number = position[0]
                time_slot = position[1]
                room = gi.class_rooms_dict[room_fi_number]
                if hc.course_event_fits_into_time_slot(
                        event, time_slot + self.timetable.offset *
                        40) and hc.room_capacity_constraint(event, room):
                    self.timetable.assign_course_to_position(event, position)
                    events_to_remove.append(event)
                    break

        # removed assigned events
        for event in events_to_remove:
            self.events.remove(event)

        distance = len(self.events)
        delta_e = distance - self.last_distance

        if delta_e > 0:
            self.timetable = timetable_back_up
            self.events = events_back_up
            return False
        # Success!
        self.last_distance = distance
        if self.last_distance < self.best_distance:
            self.best_feasible_tt = copy.deepcopy(self.timetable)
            self.best_distance = distance
        return True
Example #3
0
def swap_positions(timetable, events, position_1, position_2, feasibility=True):
    """
    :param timetable: instance of Timetable
    :param events: list of courseEvents
    :param position_1: (fi_number, time_slot)
    :param position_2: (fi_number, time_slot)
    :param feasibility: boolean that indicates if feasibility should be preserved
    :return:
    """
    event_1 = timetable.timetable[position_1]
    fi_number_1 = position_1[0]
    room_1 = gi.class_rooms_dict[fi_number_1]
    time_slot_1 = position_1[1]
    event_2 = timetable.timetable[position_2]
    fi_number_2 = position_2[0]
    room_2 = gi.class_rooms_dict[fi_number_2]
    time_slot_2 = position_2[1]
    # Check if both events are not None or that they are not equal
    if (event_1 is None and event_2 is None) or event_1 == event_2:
        return False, timetable, events

    # if feasibility should be preserved, the positions only get swapped if the swap is possible for both
    if feasibility:
        # Check if no hard constraints get violated
        if event_1 is not None:
            timetable.remove_course_from_position(position_2)
            swap_possible = hc.course_event_fits_into_time_slot(event_1, time_slot_2+timetable.offset*40) and hc.room_capacity_constraint(event_1, room_2)
            timetable.assign_course_to_position(event_2, position_2)
            if not swap_possible:
                return False, timetable, events

        if event_2 is not None:
            timetable.remove_course_from_position(position_1)
            swap_possible = hc.course_event_fits_into_time_slot(event_2, time_slot_1+timetable.offset*40) and hc.room_capacity_constraint(event_2, room_1)
            timetable.assign_course_to_position(event_1, position_1)
            if not swap_possible:
                return False, timetable, events

        # swapping is possible, so the positions of the two events will get swapped
        timetable.remove_course_from_position(position_1)
        timetable.remove_course_from_position(position_2)
        timetable.assign_course_to_position(event_1, position_2)
        timetable.assign_course_to_position(event_2, position_1)
        return True, timetable, events

    # if feasibility is false, the swap will occur if no hard constraints are broken for at least one event
    # the other event will get swapped if possible, or get appended the the unplaced_list if not
    timetable.remove_course_from_position(position_2)
    timetable.remove_course_from_position(position_1)
    if event_1 is not None:
        swap_possible = hc.course_event_fits_into_time_slot(event_1, time_slot_2+timetable.offset*40) and hc.room_capacity_constraint(event_1, room_2)
        if swap_possible:
            timetable.assign_course_to_position(event_1, position_2)
        else:
            events.append(event_1)
    if event_2 is not None:
        swap_possible = hc.course_event_fits_into_time_slot(event_2, time_slot_1+timetable.offset*40) and hc.room_capacity_constraint(event_2, room_1)
        if swap_possible:
            timetable.assign_course_to_position(event_2, position_1)
        else:
            events.append(event_2)
    return True, timetable, events
Example #4
0
def swap_occupied_for_unplaced_in_time_slot(timetable, events, time_slot):
    """
    This function will look at all occupied places in the timetable for the given time_slot
    and swap an entry with an event in the events list.
    :param timetable: instance of Timetable
    :param events: list of events
    :param time_slot: the time slot that will be checked
    :return: timetable, events
    """

    for occupied_position in timetable.occupied_positions:
        if occupied_position[1] != time_slot:
            continue
        event_back_up = timetable.remove_course_from_position(occupied_position)
        timetable.assign_course_to_position(event_back_up, occupied_position)
        room = gi.class_rooms_dict[occupied_position[0]]
        time_slot = occupied_position[1]
        found_replacement = False
        for event in events:
            if hc.course_event_fits_into_time_slot(event, time_slot+timetable.offset*40) and hc.room_capacity_constraint(event, room):
                timetable.assign_course_to_position(event, occupied_position)
                found_replacement = True
                break
        if not found_replacement:
            timetable.assign_course_to_position(event_back_up, occupied_position)
            continue
        if found_replacement:
            events.append(event_back_up)
            return True, timetable, events
    return False, timetable, events
    def split_event(self, tabu_list):
        # sort events by largest student amount
        self.events.sort(key=lambda ev: ev.student_amount, reverse=True)
        # get the course with the most amount of students
        event = self.events.pop(0)
        # check if this event is not in the tabu list
        max_events = len(self.events)
        index = 0
        while event in tabu_list and index < max_events:
            self.events.append(event)
            event = self.events.pop(0)
            index += 1

        # get all available positions, not taking in account the room capacity
        biggest_capacity = 0
        for empty_position in self.timetable.empty_positions:
            fi_number = empty_position[0]
            time_slot = empty_position[1]
            if hc.course_event_fits_into_time_slot(
                    event, time_slot + self.timetable.offset * 40):
                size = gi.class_rooms_dict[fi_number].capacity
                if size > biggest_capacity:
                    biggest_capacity = size

        if biggest_capacity == 0:
            tabu_list.append(event)
            self.events.append(event)
            return
        # split the event
        course_code = event.course_code
        lecturers = event.lecturers
        student_amount_1 = biggest_capacity
        student_amount_2 = event.student_amount - student_amount_1
        curricula = event.curricula
        event_number = event.event_number
        course = gi.courses_dict[course_code]
        course.course_hours += 1  # because the event is split into two, an extra course hour should be created
        event_1 = data.CourseEvent(course_code=course_code,
                                   lecturers=lecturers,
                                   student_amount=student_amount_1,
                                   curricula=curricula,
                                   event_number=event_number)
        event_2 = data.CourseEvent(course_code=course_code,
                                   lecturers=lecturers,
                                   student_amount=student_amount_2,
                                   curricula=curricula,
                                   event_number=event_number)
        # add the new events to the tabu list
        tabu_list.append(event_1)
        tabu_list.append(event_2)
        self.events.insert(0, event_1)
        self.events.insert(1, event_2)

        # check if it is possible to place extra events
        events_to_remove = []
        for event in self.events:
            for position in self.timetable.empty_positions:
                room_fi_number = position[0]
                time_slot = position[1]
                room = gi.class_rooms_dict[room_fi_number]
                if hc.course_event_fits_into_time_slot(
                        event, time_slot + self.timetable.offset *
                        40) and hc.room_capacity_constraint(event, room):
                    self.timetable.assign_course_to_position(event, position)
                    events_to_remove.append(event)
                    break
        # remove the events that got assigned
        for event in events_to_remove:
            self.events.remove(event)

        distance = len(self.events)
        self.last_distance = distance
        if self.last_distance <= self.best_distance:
            self.best_feasible_tt = copy.deepcopy(self.timetable)
            self.best_distance = distance
        return