def trim_group(G, students_left, stress_limit, group):
    curr_stress = calculate_stress_for_room(group, G)
    while (curr_stress > stress_limit):
        best_removal = 0
        removed = group[0]
        for x in range(len(group)):
            student = group[x]
            group.remove(student)
            happiness = calculate_happiness_for_room(group, G)
            stress = calculate_stress_for_room(group, G)

            if stress == 0:
                removed = student
                group.append(student)
                break

            if ((happiness / stress) > best_removal):
                best_removal = happiness / stress
                removed = student
                # print("best_removal: " + str(best_removal))
                # print("removed:      "  + str(removed))
            group.append(student)

        group.remove(removed)
        students_left.append(removed)
        curr_stress = calculate_stress_for_room(group, G)
Example #2
0
def findopt(G_new, G, s, groups, curr_stress):
    """
    mutates the networkx graphs 'G' and 'groups', and
    returns the increase in total stress.
    """
    students = G_new.number_of_nodes()
    max_value = 0
    merge_nodes = ('a', 'a')
    # Check all edges, find the best one to merge
    new_stress_capacity = s / (groups.number_of_nodes() - 1
                               )  # new stress capacity if a
    # merge is made
    remaining_stress = new_stress_capacity - curr_stress
    for edge in list(G_new.edges):
        i = edge[0]
        j = edge[1]
        if G_new.get_edge_data(i, j) is not None:
            edge_happiness = G_new.get_edge_data(i, j)['happiness']
            edge_stress = G_new.get_edge_data(i, j)['stress']
            if edge_stress <= remaining_stress:
                value = heuristic(edge_stress, edge_happiness,
                                  remaining_stress)
                if value > max_value:
                    max_value = value
                    merge_nodes = (i, j)
            else:
                continue

    # Calculate the increase in stress
    i, j = merge_nodes[0], merge_nodes[1]
    if i == 'a' or j == 'a':
        return 'done'
    old_stress = calculate_stress_for_room(groups.nodes[i]['ppl'], G)
    copy = groups.copy()
    copy.nodes[i]['ppl'] += copy.nodes[j]['ppl']
    if calculate_stress_for_room(copy.nodes[i]['ppl'], G) > G.number_of_nodes() \
            / s:
        return 0
    else:
        groups.nodes[i]['ppl'] += groups.nodes[j]['ppl']
    groups.remove_node(j)
    new_stress = calculate_stress_for_room(groups.nodes[i]['ppl'], G)

    # Merge the 2 nodes from chosen edge, update G
    l1 = list(G_new.edges(i))
    l1.remove((i, j))
    l2 = list(G_new.edges(j))
    l2.remove((j, i))
    n = len(l1)
    for i in range(n):
        G_new.get_edge_data(l1[i][0], l1[i][1])['happiness'] += \
            G_new.get_edge_data(l2[i][0], l2[i][1])['happiness']
        G_new.get_edge_data(l1[i][0], l1[i][1])['stress'] += \
            G_new.get_edge_data(l2[i][0], l2[i][1])['stress']
    G_new.remove_node(j)

    return 0
Example #3
0
def solve(G, s):
    """
    Args:
        G: networkx.Graph
        s: stress_budget
    Returns:
        D: Dictionary mapping for student to breakout room r e.g. {0:2, 1:0, 2:1, 3:2}
        k: Number of breakout rooms
    """
    g_new = G.copy()
    groups = nx.Graph()  # Graph to store groupings of students
    for i in range(G.number_of_nodes()):
        groups.add_node(i, ppl=[i])
    curr_stress = 0  # Total stress from room assignment
    max_stress, remaining_stress = s, s
    while remaining_stress >= 0:  # While remaining stress is greater than 0
        curr_stress = findopt(g_new, G, s, groups, curr_stress)
        if curr_stress == 'done':
            break
        for node in list(groups.nodes):
            curr_stress += calculate_stress_for_room(groups.nodes[node]['ppl'],
                                                     G)
        max_stress = s / groups.number_of_nodes()
        remaining_stress = max_stress - curr_stress

    k = groups.number_of_nodes()
    # Convert the groupings into a dictionary
    d = dict()
    nodes = list(groups.nodes)
    for i in range(k):
        for student in groups.nodes[nodes[i]]['ppl']:
            d[student] = i
    d = dict(sorted(d.items(), key=lambda x: x[0]))
    return d, k
def solve(G, s):
    """
    Args:
        G: networkx.Graph
        s: stress_budget
    Returns:
        D: Dictionary mapping for student to breakout room r e.g. {0:2, 1:0, 2:1, 3:2}
        k: Number of breakout rooms
    """
    D = {}
    for i in G.nodes:
        D[i] = 0
    rooms = 1
    while not is_valid_solution(D, G, s, rooms):
        # print(rooms)
        for i in range(rooms):
            people_in_room_i = [
                person for person in D.keys() if D[person] == i
            ]
            while calculate_stress_for_room(people_in_room_i, G) > s / rooms:
                person_to_take_out = people_in_room_i[random.randrange(
                    0,
                    len(people_in_room_i) - 1, 1)]
                D[person_to_take_out] = i + 1
                if (i + 1) > rooms - 1:
                    rooms = rooms + 1
                people_in_room_i.remove(person_to_take_out)
    return D, rooms
Example #5
0
def solve_naive(G, s):
    vertex_list = list(G.nodes)
    N = len(vertex_list)

    best_happiness = 0
    config = {}
    numRooms = 0

    for n, p in enumerate(partition(list(range(20))), 1):
        isValid = True
        for room in p:
            if calculate_stress_for_room(room, G) > s / len(p):
                isValid = False
                break
        print(n)
        if not isValid:
            continue
        
        D = {}
        for i in range(len(p)):
            for j in p[i]:
                D[j] = i

        happiness = calculate_happiness(D, G)
        if happiness > best_happiness:
            best_happiness = happiness
            config = D
            numRooms = len(p)

    return config, numRooms
Example #6
0
def solve_greedy(G, s):
    vertex_list = list(G.nodes)
    N = len(vertex_list)

    # iterate through all possible values of number of rooms
    for k in range(1, N+1):
        solution = {}  # key:value => (vertex, room)
        room_to_vertices = {}  # key:value => (room number, vertices)

        # Assume all vertices are on their own 
        for i in range(len(vertex_list)):
            solution[i] = i
            room_to_vertices[i] = [i]
        
        edges = sorted(G.edges(data=True), key = sortOrder, reverse = True)
        # edges = sorted(G.edges(data=True), key=lambda t: t[2].get('happiness'), reverse = True) # sort by highest happiness

        for e in edges:
            # get current room numbers
            room_v = solution[e[0]]
            room_u = solution[e[1]]

            # see if the pair is already in the same room
            if room_u == room_v:
                continue
            
            # attempt to merge room
            merged_room = room_to_vertices[room_v] + room_to_vertices[room_u] 

            # if it does not satisfy stress constraints, abort
            if calculate_stress_for_room(merged_room, G) > s / k:
                continue
            
            # actually merge
            for v in room_to_vertices[room_u]:
                solution[v] = room_v
            room_to_vertices[room_v] = merged_room
            room_to_vertices.pop(room_u)

        # print(calculate_happiness(solution, G))

        # check to see if we should update if the constraint on the # of rooms is satisfied; if so, return the result
        if len(room_to_vertices) <= k:
            result = {}
            length = len(room_to_vertices)

            for i in range(length):
                vertices = room_to_vertices.pop(list(room_to_vertices.keys())[0])
                for v in vertices:
                    result[v] = i
            return result, length
Example #7
0
def solve(G, s):
    """
    Args:
        G: networkx.Graph
        s: stress_budget
    Returns:
        D: Dictionary mapping for student to breakout room r e.g. {0:2, 1:0, 2:1, 3:2}
        k: Number of breakout rooms
    """

    # TODO: your code here!
    D = {}
    rooms = {}
    k = G.number_of_nodes()
    for i in range(G.number_of_nodes()):
        D[i] = i
        rooms[i] = [i]
    changed = []
    for i in range(G.number_of_nodes()-2):
        for j in range(G.number_of_nodes()):
            if j in list(rooms):
                max_happy = 0
                min_stress = s / (len(rooms) - 1)
                curr_min_stress = min_stress
                reee = 0
                for asdf in list(G[j]):
                    if calculate_stress_for_room(rooms[D[j]] + [asdf], G) < min_stress and (calculate_stress_for_room(rooms[D[j]] + [asdf], G) < curr_min_stress or calculate_happiness_for_room(rooms[D[j]] + [asdf], G) > max_happy):
                        max_happy = calculate_happiness_for_room(rooms[D[j]] + [asdf], G)
                        curr_min_stress = calculate_stress_for_room(rooms[D[j]] + [asdf], G)
                        reee = asdf
                if reee and reee not in changed:
                    changed.append(reee)
                    D[reee] = j
                    rooms[j].append(reee)
                    del rooms[reee]
    return D, len(rooms)
Example #8
0
    def move(index):
        room = rooms[index]
        # if room satisfies stress requirement
        if calculate_stress_for_room(room, G) <= s / len(rooms):
            return
 
        # person to be removed
        kick = process(index)
        # remove person
        rooms[index].remove(kick)
        # create new room if necessary
        if index == len(rooms) - 1:
            rooms.append([])
        # add person to new room
        rooms[index + 1].append(kick)
        # check next room for validity
        move(index + 1)
def find_best_addition(G, students_left, stress_limit, group):
    max_ratio = 0
    max_addition = -1
    for x in range(len(students_left)):
        group.append(students_left[x])
        happiness = calculate_happiness_for_room(group, G)
        stress = calculate_stress_for_room(group, G)

        if stress == 0:
            return students_left[x]

        if (stress < stress_limit and (happiness / stress) > max_ratio):
            max_addition = students_left[x]
            max_ratio = happiness / stress
            # print(max_addition)
            # print(max_ratio)
        group.remove(students_left[x])

    return max_addition
Example #10
0
def solve(G, s, swap):
    """
    Args:
        G: networkx.Graph
        s: stress_budget
    Returns:
        D: Dictionary mapping for student to breakout room r e.g. {0:2, 1:0, 2:1, 3:2}
        k: Number of breakout rooms
    """

    # TODO: your code here!
    rooms = [list(G.nodes)]

    def move(index):
        room = rooms[index]
        # if room satisfies stress requirement
        if calculate_stress_for_room(room, G) <= s / len(rooms):
            return
 
        # person to be removed
        kick = process(index)
        # remove person
        rooms[index].remove(kick)
        # create new room if necessary
        if index == len(rooms) - 1:
            rooms.append([])
        # add person to new room
        rooms[index + 1].append(kick)
        # check next room for validity
        move(index + 1)
        
         
    def process(index):
        r = rooms[index]
        ret = {}
        for i in r:
            ret[i] = 0
            for j in r:
                if i == j:
                    continue
                if swap == 0:
                    ret[i] += G.edges[i,j]["happiness"] - G.edges[i,j]["stress"]
                elif swap == 1:
                    ret[i] += G.edges[i,j]["happiness"]
                else:
                    ret[i] -= G.edges[i,j]["stress"]
        return min(ret, key = lambda x: ret[x])


    finished = False
    while not finished:
        finished = True
        for i in range(len(rooms)):
            room = rooms[i]
            if calculate_stress_for_room(room, G) > s / len(rooms):
                move(i)
                finished = False
                break

    D = {}
    for i in range(len(rooms)):
        r = rooms[i]
        for n in r:
            D[n] = i
    k = len(rooms)
    ret = D

    max_happiness = calculate_happiness(ret, G)
    improved = True
    # print("before", max_happiness)

    # def search_local(depth):
    #     nonlocal D
    #     nonlocal k
    #     nonlocal improved
    #     nonlocal max_happiness
    #     nonlocal ret
    #     if depth == 0:
    #         return
    #     for i in D:
    #         for j in D:
    #             if i == j:
    #                 continue
    #             D[i], D[j] = D[j], D[i]
    #             kkk = len(set(D.values()))
    #             if is_valid_solution(D, G, s, kkk):
    #                 happy = calculate_happiness(D, G)
    #                 if happy > max_happiness:
    #                     k = kkk
    #                     max_happiness = happy
    #                     ret = {}
    #                     for key in D:
    #                         ret[key] = D[key]
    #                     improved = True
    #                 search_local(depth - 1)
    #             D[i], D[j] = D[j], D[i]

    #             temp, D[i] = D[i], D[j]
    #             kkk = len(set(D.values()))
    #             if is_valid_solution(D, G, s, kkk):
    #                 happy = calculate_happiness(D, G)
    #                 if happy > max_happiness:
    #                     k = kkk
    #                     max_happiness = happy
    #                     ret = {}
    #                     for key in D:
    #                         ret[key] = D[key]
    #                     improved = True
    #                 search_local(depth - 1)
    #             D[i] = temp

    while improved:
        improved = False
        # search_local(2)
        # D = ret
        for i in D:
            for j in D:
                if i == j:
                    continue
                D[i], D[j] = D[j], D[i]
                kkk = len(set(D.values()))
                if is_valid_solution(D, G, s, kkk):
                    if calculate_happiness(D, G) > max_happiness:
                        max_happiness = calculate_happiness(D, G)
                        ret = {}
                        for key in D:
                            ret[key] = D[key]
                        improved = True
                        k = kkk
                D[i], D[j] = D[j], D[i]

                temp, D[i] = D[i], D[j]
                kkk = len(set(D.values()))
                if is_valid_solution(D, G, s, kkk):
                    if calculate_happiness(D, G) > max_happiness:
                        max_happiness = calculate_happiness(D, G)
                        ret = {}
                        for key in D:
                            ret[key] = D[key]
                        improved = True
                        k = kkk
                D[i] = temp
        D = ret
    # print("after:", max_happiness)
    return ret, k, max_happiness
Example #11
0
def greedy_solve_1(G, s):
    """
    Probably open 1 room, add as many possible students w/o exceeding stress level, then
    if s_level exceeded, remove all students, open 2 rooms, repeat
     - Remember to sort students (or sort edgelist or somt) and break ties by happiness

    Helpful utils:
        utils.is_valid_solution(D, G, s, rooms)
        utils.convert_dictionary(room_to_student)
        utils.calculate_stress_for_room(arr, G)
        utils.calculate_happiness_for_room(arr, G)
    """
    #Iterate by opening 1 breakout room at a time, up to n breakout rooms
    room_to_students_to_return = {}
    max_happy = -1
    for i in range(1, len(list(G.nodes)) + 1):

        room_to_students = {}
        for j in range(i):
            room_to_students[j] = []

        #print("room_to_students: ", room_to_students)
        #Make copy of graph (so we can use actual graph later on as reference)
        G_copy = nx.Graph(G)

        #Create edgeList pairs of students sorted by stress/happiness
        stress_edgeList = sorted(G_copy.edges, key=lambda x: G_copy.edges[x]["stress"], reverse = True)
        happy_edgeList = sorted(G_copy.edges, key=lambda x: G_copy.edges[x]["happiness"], reverse = True)

        #dictionary of happiness values to list of all students that have same happiness value
        happy_dict = {}
        for edge in happy_edgeList:
            #print("edge: ", edge)
            #print("happiness: ", G_copy.edges[edge]["happiness"])
            if G_copy.edges[edge]["happiness"] in happy_dict:
                happy_dict[G_copy.edges[edge]["happiness"]] += [edge]
            else:
                happy_dict[G_copy.edges[edge]["happiness"]] = []
                happy_dict[G_copy.edges[edge]["happiness"]] += [edge]

        assigned_students = []
        #Assign students until all pairings are broken or assigned (i.e. all students in rooms)
        while (len(assigned_students) < len(list(G.nodes))):


            #Take happiest pair and try to assign them to rooms to maximize happiness
            #print(happy_dict)
            student_pair = None

            """
            for key in sorted(happy_dict.keys()):
                #print("key: ", key)
                #print("happy_dict[key]: ", happy_dict[key])
                if (len(happy_dict[key]) > 0):

                    student_pair = random.sample(happy_dict[key], 1)
                    #print(student_pair[0])
                    happy_dict[key].remove(student_pair[0])

            #student_pair = happy_edgeList.pop(0)


            if (not student_pair):
                print("here")
                for key in sorted(happy_dict.keys()):
                    print("key: ", key)
                    if (len(happy_dict[key]) > 0):

                        print("happy_dict[key]: ", happy_dict[key])

                break

            student_pair = student_pair[0]
            """

            pop_amt = min(len(happy_edgeList), 10)
            if (pop_amt <= 0):
                break
            student_pair = happy_edgeList.pop(random.randint(0, pop_amt-1))
            #print("num assigend students: ", len(assigned_students))
            #print("student_pair: ", student_pair)
            #print("happy val: ", G_copy.edges[student_pair]["happiness"])
            student0 = student_pair[0]
            student1 = student_pair[1]


            #Check if students have been assigned
            student0_assigned = (student0 in assigned_students)
            student1_assigned = (student1 in assigned_students)
            #If students are already assigned (whether in same or diff room), go to next happiest pair
            if (student0_assigned and student1_assigned):
                #print("here0")
                continue





            #Check which room the students are in, if they have already been asigned to a room
            room_of_student0 = -1
            room_of_student1 = -1
            for room in room_to_students:
                if student0 in room_to_students[room]:
                    room_of_student0 = room
                if student1 in room_to_students[room]:
                    room_of_student1 = room

            #If student0 assigned, try to put student1 in same room, else put in room that causes least stress
            if (student0_assigned):
                #print("here1")
                room_to_students[room_of_student0] += [student1]
                assigned_students += [student1]
                valid0 = utils.is_valid_solution(utils.convert_dictionary(room_to_students), G, s, i)
                if (valid0):
                    continue

                #Can't put student1 in same room, so try to put in diff room
                room_to_students[room_of_student0].remove(student1)
                assigned_students.remove(student1)

                min_stress1 = float('inf')
                min_room1 = -1
                for room in room_to_students:
                    room_to_students[room] += [student1]

                    #check if adding students to this room causes minimum stress (disruption?)
                    temp_stress1 = utils.calculate_stress_for_room(room_to_students[room], G)
                    #check if solution is valid when adding students to this room
                    valid1 = utils.is_valid_solution(utils.convert_dictionary(room_to_students), G, s, i)
                    if (temp_stress1 < min_stress1 and valid1):
                        min_stress1 = temp_stress1
                        min_room1 = room

                    room_to_students[room].remove(student1)

                if (min_room1 >= 0):
                    room_to_students[min_room1] += [student1]
                    assigned_students += [student1]
                else:
                # at this point, student1 cant be assigned to any room without causing excess stress,
                # so this solution/number of breakout rooms cannot work, so try opening more rooms
                    #print("I got here2, assigned_students = ", assigned_students)
                    break
                    #continue

            #If student1 assigned, try to put student0 in same room, else put in room that causes least stress
            if (student1_assigned):
                #print("here2")
                room_to_students[room_of_student1] += [student0]
                assigned_students += [student0]
                valid1 = utils.is_valid_solution(utils.convert_dictionary(room_to_students), G, s, i)
                if (valid1):
                    continue

                #Can't put student1 in same room, so try to put in diff room
                room_to_students[room_of_student1].remove(student0)
                assigned_students.remove(student0)

                min_stress0 = float('inf')
                min_room0 = -1
                for room in room_to_students:
                    room_to_students[room] += [student0]

                    #check if adding students to this room causes minimum stress (disruption?)
                    temp_stress0 = utils.calculate_stress_for_room(room_to_students[room], G)
                    #check if solution is valid when adding students to this room
                    valid0 = utils.is_valid_solution(utils.convert_dictionary(room_to_students), G, s, i)
                    if (temp_stress0 < min_stress0 and valid0):
                        min_stress0 = temp_stress0
                        min_room0 = room

                    room_to_students[room].remove(student0)

                if (min_room0 >= 0):
                    room_to_students[min_room0] += [student0]
                    assigned_students += [student0]
                else:
                # at this point, student1 cant be assigned to any room without causing excess stress,
                # so this solution/number of breakout rooms cannot work, so try opening more rooms
                    #print("I got here4, assigned_students = ", assigned_students)
                    break
                    #continue


            if (not student1_assigned and not student0_assigned):
                #print("here5")

                #If neither student assigned, put both into the breakout room that creates least stress
                #If putting them into a breakout room together always creates invalid solution, consider putting them into seperate rooms
                min_stress = float('inf')
                min_room = -1
                for room in room_to_students:
                    room_to_students[room] += [student0]
                    room_to_students[room] += [student1]

                    #check if adding students to this room causes minimum stress (disruption?)
                    temp_stress = utils.calculate_stress_for_room(room_to_students[room], G)
                    #check if solution is valid when adding students to this room
                    valid = utils.is_valid_solution(utils.convert_dictionary(room_to_students), G, s, i)
                    if (temp_stress < min_stress and valid):
                        min_stress = temp_stress
                        min_room = room

                    room_to_students[room].remove(student0)
                    room_to_students[room].remove(student1)

                if (min_room >= 0):
                    room_to_students[min_room] += [student0]
                    room_to_students[min_room] += [student1]
                    assigned_students += [student0]
                    assigned_students += [student1]
                    continue

                #if putting students together in breakout room still causes excess stress, put them in diff rooms
                min_stress0 = float('inf')
                min_room0 = -1

                for room in room_to_students:
                    room_to_students[room] += [student0]

                    #check if adding students to this room causes minimum stress (disruption?)
                    temp_stress0 = utils.calculate_stress_for_room(room_to_students[room], G)
                    #check if solution is valid when adding students to this room
                    valid0 = utils.is_valid_solution(utils.convert_dictionary(room_to_students), G, s, i)
                    if (temp_stress0 < min_stress0 and valid0):
                        min_stress0 = temp_stress0
                        min_room0 = room

                    room_to_students[room].remove(student0)



                min_stress1 = float('inf')
                min_room1 = -1

                for room in room_to_students:
                    room_to_students[room] += [student1]

                    #check if adding students to this room causes minimum stress (disruption?)
                    temp_stress1 = utils.calculate_stress_for_room(room_to_students[room], G)
                    #check if solution is valid when adding students to this room
                    valid1 = utils.is_valid_solution(utils.convert_dictionary(room_to_students), G, s, i)
                    if (temp_stress1 < min_stress1 and valid1):
                        min_stress1 = temp_stress1
                        min_room1 = room

                    room_to_students[room].remove(student1)

                if (min_room1 >= 0):
                    room_to_students[min_room1] += [student1]
                    assigned_students += [student1]
                if (min_room0 >= 0):
                    room_to_students[min_room0] += [student0]
                    assigned_students += [student0]
                else:
                # at this point, student0 cant be assigned to any room without causing excess stress,
                # so this solution/number of breakout rooms cannot work, so try opening more rooms
                    #print("I got here, assigned_students = ", assigned_students)
                    break
                    #continue













        #print("here3")

        valid_sol = utils.is_valid_solution(utils.convert_dictionary(room_to_students), G, s, i)
        happy = utils.calculate_happiness(utils.convert_dictionary(room_to_students), G)
        if (len(assigned_students) < len(list(G.nodes))):
            #print(room_to_students)
            happy = float('-inf')

        #print("room_to_students: ", room_to_students)
        print("happy for ", i, " rooms: ", happy)

        if (happy > max_happy and valid_sol):
            max_happy = happy
            room_to_students_to_return = {}
            for room in room_to_students:
                room_to_students_to_return[room] = room_to_students[room].copy()

    #print("here4")
    return room_to_students_to_return
Example #12
0
def remove_students_greedy(G, G_copy, s, room_to_students, value_dict,
                           assigned_students, value_edgeList, alpha, beta):
    # Check which rooms exceed stress limit
    #print("in remove_students_greedy")
    counter = 0
    while True:
        #print("here: ", counter)
        room_numbers_that_exceed_stress = []
        for room in room_to_students:
            s_room = utils.calculate_stress_for_room(room_to_students[room], G)
            if (s_room >= (s / (len(room_to_students)))
                ):  #ToDo: should this be i or i+1? Think of 50 room case
                room_numbers_that_exceed_stress += [room]

        # From the rooms that exceed stress limit, remove students until all stress limit not exceeded
        removed_students = []
        for room in room_numbers_that_exceed_stress:
            s_room = utils.calculate_stress_for_room(room_to_students[room], G)
            while (s_room >= (s / (len(room_to_students)))):
                min_stress = float("inf")
                student_to_remove = -1
                for i in range(len(room_to_students[room])):

                    student = room_to_students[room].pop(i)
                    if (utils.calculate_stress_for_room(
                            room_to_students[room], G) < min_stress):
                        min_stress = utils.calculate_stress_for_room(
                            room_to_students[room], G)
                        student_to_remove = student
                    room_to_students[room].insert(i, student)

                room_to_students[room].remove(student_to_remove)
                removed_students += [student_to_remove]
                s_room = utils.calculate_stress_for_room(
                    room_to_students[room], G)

        for i in range(len(removed_students) - 1):
            for j in range(i + 1, len(removed_students)):
                edge = (removed_students[i], removed_students[j])
                if (edge[0] == edge[1]):
                    continue
                #print(edge)
                value_edgeList += [edge]
                val = alpha * G_copy.edges[edge][
                    "happiness"] - beta * G_copy.edges[edge]["happiness"]
                if val in value_dict:
                    value_dict[val] += [edge]
                else:
                    value_dict[val] = []
                    value_dict[val] += [edge]

        value_edgeList = sorted(
            value_edgeList,
            key=lambda x: alpha * G_copy.edges[x][
                "happiness"] - beta * G_copy.edges[x]["happiness"],
            reverse=True)

        for student in removed_students:
            assigned_students.remove(student)

        #open another room
        if (len(room_to_students) < 50):
            room_to_students[len(room_to_students)] = []

        counter += 1
        if (utils.is_valid_solution(utils.convert_dictionary(room_to_students),
                                    G, s, len(room_to_students))):
            #print("why you always breakin")
            #print("This is valid? wyd: ", room_to_students)
            #print("How did you get this kinda happiness?: ", utils.calculate_happiness(utils.convert_dictionary(room_to_students), G))
            #print("Did you assign everyone tho: ", assigned_students)
            #print("Did you assign everyone tho pt 2: ", len(assigned_students))
            break
    #print("done with remove_students_greedy")
    return value_edgeList
Example #13
0
def greedy_solve_5(G, s, alpha, beta, pop_limit=5):
    """
    Probably open 1 room, add as many possible students w/o exceeding stress level, then
    if s_level exceeded, remove students that cause most stress, open 2 room, repeat
     - Remember to sort students (or sort edgelist or somt) and break ties by happiness
     - Will probably need algo to determine which student causes most stress in given room
    Helpful Utils:
     - utils.calculate_stress_for_room(arr, G)
    """
    #Iterate by opening 1 breakout room at a time, up to n breakout rooms
    room_to_students_to_return = {}
    max_happy = -1
    assigned_students = []
    i = 1
    room_to_students = {}
    for j in range(i):
        room_to_students[j] = []

    #print("room_to_students: ", room_to_students)
    #Make copy of graph (so we can use actual graph later on as reference)
    G_copy = nx.Graph(G)

    #Create edgeList pairs of students sorted by stress/happiness
    stress_edgeList = sorted(G_copy.edges,
                             key=lambda x: G_copy.edges[x]["stress"])
    happy_edgeList = sorted(G_copy.edges,
                            key=lambda x: G_copy.edges[x]["happiness"],
                            reverse=True)
    value_edgeList = sorted(G_copy.edges,
                            key=lambda x: alpha * G_copy.edges[x]["happiness"]
                            - beta * G_copy.edges[x]["stress"],
                            reverse=True)
    #Todo: Maybe sort/solve based on some values/prioerities i.e. a * happy - b * stress, a & b can be constants passed in or randInts

    #dictionary of happiness values to list of all students that have same happiness value
    value_dict = {}
    for edge in value_edgeList:
        #print("edge: ", edge)
        #print("happiness: ", G_copy.edges[edge]["happiness"])
        val = alpha * G_copy.edges[edge]["happiness"] - beta * G_copy.edges[
            edge]["stress"]
        if val in value_dict:
            value_dict[val] += [edge]
        else:
            value_dict[val] = []
            value_dict[val] += [edge]

    while (len(assigned_students) < len(list(G.nodes))):

        stress_edgeList = sorted(stress_edgeList,
                                 key=lambda x: G_copy.edges[x]["stress"],
                                 reverse=True)
        happy_edgeList = sorted(happy_edgeList,
                                key=lambda x: G_copy.edges[x]["happiness"],
                                reverse=True)
        value_edgeList = sorted(
            G_copy.edges,
            key=lambda x: alpha * G_copy.edges[x][
                "happiness"] - beta * G_copy.edges[x]["stress"],
            reverse=True)

        #dictionary of happiness values to list of all students that have same happiness value
        value_dict = {}
        for edge in value_edgeList:
            #print("edge: ", edge)
            #print("happiness: ", G_copy.edges[edge]["happiness"])
            val = alpha * G_copy.edges[edge][
                "happiness"] - beta * G_copy.edges[edge]["stress"]
            if val in value_dict:
                value_dict[val] += [edge]
            else:
                value_dict[val] = []
                value_dict[val] += [edge]

        #Assign students until all pairings are broken or assigned (i.e. all students in rooms)
        while (len(assigned_students) < len(list(G.nodes))):

            #Take happiest pair and try to assign them to rooms to maximize happiness
            #print(happy_dict)
            student_pair = None
            """
            for key in sorted(happy_dict.keys()):
                #print("key: ", key)
                #print("happy_dict[key]: ", happy_dict[key])
                if (len(happy_dict[key]) > 0):

                    student_pair = random.sample(happy_dict[key], 1)
                    #print(student_pair[0])
                    happy_dict[key].remove(student_pair[0])

            #student_pair = happy_edgeList.pop(0)

            # No more student pairs – shouldn't really hit this point tho??
            if (not student_pair):
                print("here")
                #for key in sorted(happy_dict.keys()):
                    #print("key: ", key)
                    #if (len(happy_dict[key]) > 0):

                        #print("happy_dict[key]: ", happy_dict[key])

                break

            student_pair = student_pair[0]
            """
            #Todo: Does this always pick happiest?
            pop_amt = min(len(value_edgeList), pop_limit)
            if (pop_amt <= 0):
                break
            #print("assigned_students: ", assigned_students)
            #print("room_to_students: ", room_to_students)
            #print("happy_edgeList: ", happy_edgeList)
            student_pair = value_edgeList.pop(random.randint(
                0, pop_amt - 1))  # ToDo: Maybe increase this value to 5 - 10?
            #print("num assigend students: ", len(assigned_students))
            #print("student_pair: ", student_pair)
            #print("happy val: ", G_copy.edges[student_pair]["happiness"])
            student0 = student_pair[0]
            student1 = student_pair[1]

            #Check if students have been assigned
            student0_assigned = (student0 in assigned_students)
            student1_assigned = (student1 in assigned_students)
            #If students are already assigned (whether in same or diff room), go to next happiest pair
            if (student0_assigned and student1_assigned):
                #print("here0")
                continue

            #Check which room the students are in, if they have already been asigned to a room
            room_of_student0 = -1
            room_of_student1 = -1
            for room in room_to_students:
                if student0 in room_to_students[room]:
                    room_of_student0 = room
                if student1 in room_to_students[room]:
                    room_of_student1 = room

            #If student0 assigned, try to put student1 in same room, else put in room that causes least stress
            if (student0_assigned):
                #print("here1")
                #print("room_of_student0: ", room_of_student0)
                room_to_students[room_of_student0] += [student1]

                assigned_students += [student1]
                valid0 = utils.is_valid_solution(
                    utils.convert_dictionary(room_to_students), G, s, i)
                if (valid0):
                    continue

                #Can't put student1 in same room, so try to put in diff room
                room_to_students[room_of_student0].remove(student1)
                assigned_students.remove(student1)

                min_stress1 = float('inf')
                min_room1 = -1
                for room in room_to_students:
                    room_to_students[room] += [student1]

                    #check if adding students to this room causes minimum stress (disruption?)
                    temp_stress1 = utils.calculate_stress_for_room(
                        room_to_students[room], G)
                    #check if solution is valid when adding students to this room
                    valid1 = utils.is_valid_solution(
                        utils.convert_dictionary(room_to_students), G, s, i)
                    if (temp_stress1 < min_stress1 and valid1):
                        min_stress1 = temp_stress1
                        min_room1 = room

                    room_to_students[room].remove(student1)

                if (min_room1 >= 0):
                    room_to_students[min_room1] += [student1]
                    assigned_students += [student1]
                else:
                    # at this point, student1 cant be assigned to any room without causing excess stress,
                    # so this solution/number of breakout rooms cannot work, so try opening more rooms
                    #print("I got here2, assigned_students = ", assigned_students)
                    #break
                    value_edgeList = remove_students_greedy(
                        G, G_copy, s, room_to_students, value_dict,
                        assigned_students, value_edgeList, alpha, beta)
                    continue

            #If student1 assigned, try to put student0 in same room, else put in room that causes least stress
            if (student1_assigned):
                #print("here2")
                #print("room_of_student1: ", room_of_student1)
                room_to_students[room_of_student1] += [student0]
                assigned_students += [student0]
                valid1 = utils.is_valid_solution(
                    utils.convert_dictionary(room_to_students), G, s, i)
                if (valid1):
                    continue

                #Can't put student1 in same room, so try to put in diff room
                room_to_students[room_of_student1].remove(student0)
                assigned_students.remove(student0)

                min_stress0 = float('inf')
                min_room0 = -1
                for room in room_to_students:
                    room_to_students[room] += [student0]

                    #check if adding students to this room causes minimum stress (disruption?)
                    temp_stress0 = utils.calculate_stress_for_room(
                        room_to_students[room], G)
                    #check if solution is valid when adding students to this room
                    valid0 = utils.is_valid_solution(
                        utils.convert_dictionary(room_to_students), G, s, i)
                    if (temp_stress0 < min_stress0 and valid0):
                        min_stress0 = temp_stress0
                        min_room0 = room

                    room_to_students[room].remove(student0)

                if (min_room0 >= 0):
                    room_to_students[min_room0] += [student0]
                    assigned_students += [student0]
                else:
                    # at this point, student1 cant be assigned to any room without causing excess stress,
                    # so this solution/number of breakout rooms cannot work, so try opening more rooms
                    #print("I got here4, assigned_students = ", assigned_students)
                    #break
                    value_edgeList = remove_students_greedy(
                        G, G_copy, s, room_to_students, value_dict,
                        assigned_students, value_edgeList, alpha, beta)
                    continue

            if (not student1_assigned and not student0_assigned):
                #print("here5")

                #If neither student assigned, put both into the breakout room that creates least stress
                #If putting them into a breakout room together always creates invalid solution, consider putting them into seperate rooms
                min_stress = float('inf')
                min_room = -1
                for room in room_to_students:
                    room_to_students[room] += [student0]
                    room_to_students[room] += [student1]

                    #check if adding students to this room causes minimum stress (disruption?)
                    temp_stress = utils.calculate_stress_for_room(
                        room_to_students[room], G)
                    #check if solution is valid when adding students to this room
                    valid = utils.is_valid_solution(
                        utils.convert_dictionary(room_to_students), G, s, i)
                    if (temp_stress < min_stress and valid):
                        min_stress = temp_stress
                        min_room = room

                    room_to_students[room].remove(student0)
                    room_to_students[room].remove(student1)

                if (min_room >= 0):
                    room_to_students[min_room] += [student0]
                    room_to_students[min_room] += [student1]
                    assigned_students += [student0]
                    assigned_students += [student1]
                    continue

                #if putting students together in breakout room still causes excess stress, put them in diff rooms
                min_stress0 = float('inf')
                min_room0 = -1

                for room in room_to_students:
                    room_to_students[room] += [student0]

                    #check if adding students to this room causes minimum stress (disruption?)
                    temp_stress0 = utils.calculate_stress_for_room(
                        room_to_students[room], G)
                    #check if solution is valid when adding students to this room
                    valid0 = utils.is_valid_solution(
                        utils.convert_dictionary(room_to_students), G, s, i)
                    if (temp_stress0 < min_stress0 and valid0):
                        min_stress0 = temp_stress0
                        min_room0 = room

                    room_to_students[room].remove(student0)

                min_stress1 = float('inf')
                min_room1 = -1

                for room in room_to_students:
                    room_to_students[room] += [student1]

                    #check if adding students to this room causes minimum stress (disruption?)
                    temp_stress1 = utils.calculate_stress_for_room(
                        room_to_students[room], G)
                    #check if solution is valid when adding students to this room
                    valid1 = utils.is_valid_solution(
                        utils.convert_dictionary(room_to_students), G, s, i)
                    if (temp_stress1 < min_stress1 and valid1):
                        min_stress1 = temp_stress1
                        min_room1 = room

                    room_to_students[room].remove(student1)

                if (min_room1 >= 0):
                    room_to_students[min_room1] += [student1]
                    assigned_students += [student1]
                if (min_room0 >= 0):
                    room_to_students[min_room0] += [student0]
                    assigned_students += [student0]
                else:
                    #continue
                    # at this point, student0 cant be assigned to any room without causing excess stress,
                    # so this solution/number of breakout rooms cannot work, so remove students
                    # that cause stress level to exceed room stress level, put them back into
                    # happiness edgelist and dictionary, and then continue from there

                    value_edgeList = remove_students_greedy(
                        G, G_copy, s, room_to_students, value_dict,
                        assigned_students, value_edgeList, alpha, beta)

                    #print("I got here, assigned_students = ", assigned_students)
                    #break
                    continue

        #print("here3")

        valid_sol = utils.is_valid_solution(
            utils.convert_dictionary(room_to_students), G, s,
            len(room_to_students))
        happy = utils.calculate_happiness(
            utils.convert_dictionary(room_to_students), G)
        if (len(assigned_students) < len(list(G.nodes))):
            #print(room_to_students)
            happy = float('-inf')

        #print("room_to_students: ", room_to_students)

        #print("room_to_students: ", room_to_students)
        #print("happy: ", happy)
        if (happy > max_happy and valid_sol):
            max_happy = happy
            room_to_students_to_return = {}
            for room in room_to_students:
                room_to_students_to_return[room] = room_to_students[room].copy(
                )
        elif (not valid_sol):
            value_edgeList = remove_students_greedy(
                G, G_copy, s, room_to_students, value_dict, assigned_students,
                value_edgeList, alpha, beta)
        #elif ((happy < max_happy) and (len(room_to_students) < 50)):
        #happy_dict = remove_students_greedy(G, G_copy, s, room_to_students, happy_dict, assigned_students, happy_edgeList)
        #else:
        #break

        if (len(value_edgeList) <= 0):
            break

    #print("here4")
    print("happy for ", len(room_to_students), " rooms: ", max_happy)
    return room_to_students_to_return
#     assert len(sys.argv) == 2
#     path = sys.argv[1]
#     G, s = read_input_file(path)
#     D, k = solve(G, s)
#     assert is_valid_solution(D, G, s, k)
#     print("Total Happiness: {}".format(calculate_happiness(D, G)))
#     write_output_file(D, 'outputs/small-1.out')

# For testing a folder of inputs to create a folder of outputs, you can use glob (need to import it)
if __name__ == '__main__':
    inputs = glob.glob('inputs/*')
    for input_path in inputs:
        print(input_path)
        output_path = 'outputs/' + basename(normpath(input_path))[:-3] + '.out'
        G, s = read_input_file(input_path)
        D, k, room_to_student = solve(G, s)

        print()
        print(room_to_student)
        print("# of rooms: " + str(k))
        print("stress limit: " + str(s / k))
        for i in range(k):
            print(
                str(i) + ": " +
                str(calculate_stress_for_room(room_to_student[i], G)))
        print()

        assert is_valid_solution(D, G, s, k)
        happiness = calculate_happiness(D, G)
        write_output_file(D, output_path)
Example #15
0
def solve_helper(G, s, ratio_num, happiness_num, stress_num):
    """
    Args:
        G: networkx.Graph
        s: stress_budget
    Returns:
        D: Dictionary mapping for student to breakout room r e.g. {0:2, 1:0, 2:1, 3:2}
        k: Number of breakout rooms
    """
    N = len(G.nodes())

    # another idea: get the best edges for each person, and iterate through some of them to connect together
    for k in range(5, N+1):
        G_copy = copy.deepcopy(G)
        i = 0  # room counter
        solution = {}  # key:value => (vertex, room)
        while True:
            if i == k:
                break
            v = choice(list(G_copy.nodes()))

            # if len(G_copy.nodes()) == 1:
            #     v = list(G_copy.nodes())[0]
            # else:
            #     edges = sorted(G_copy.edges(data=True), key = sortOrder, reverse = True)
            #     v = edges[0][0]

            best_ratios = sorted(G_copy.edges(v, data = True), key = sortOrder, reverse = True)[:ratio_num]
            highest_happiness = sorted(G_copy.edges(v, data=True), key=lambda t: t[2].get('happiness'), reverse = True)[:happiness_num]
            lowest_stress = sorted(G_copy.edges(v, data=True), key=lambda t: t[2].get('stress'))[:stress_num]

            # best_ratios_vertices = []
            # highest_happiness_vertices = []
            # lowest_stress_vertices = []
            # if len(best_ratios) != 0:
            best_ratios_vertices = set([i[1] for i in best_ratios])
            # if len(highest_happiness) != 0:
            highest_happiness_vertices = set([i[1] for i in highest_happiness])
            # if len(lowest_stress) != 0:
            lowest_stress_vertices = set([i[1] for i in lowest_stress])

            vertices = best_ratios_vertices.union(highest_happiness_vertices).union(lowest_stress_vertices)
            subsets = findsubsets(vertices)

            best_merged_room = [v]
            largest_happiness = 0
            for subset in subsets:
                merged_room = [v]
                for vertex in subset:
                    merged_room.append(vertex)
                
                # if it does not satisfy stress constraints, abort
                if calculate_stress_for_room(merged_room, G) > s / k:
                    continue

                happiness = calculate_happiness_for_room(merged_room, G)
                if happiness > largest_happiness:
                    largest_happiness = happiness
                    best_merged_room = merged_room
            
            for v in best_merged_room:
                solution[v] = i
            i += 1
            G_copy.remove_nodes_from(best_merged_room)

            if not G_copy.nodes():
                return solution, i