Exemple #1
0
 def create(nodes, mapping):
     nonlocal max_happiness
     nonlocal ret
     nonlocal k
     if nodes == []:
         mm = convert_dictionary(mapping)
         #if mapping not in rooms and is_valid_solution(mm, G, s, len(mapping)):
             #rooms.append(mapping)
         room_k = len(mapping)
         if is_valid_solution(mm, G, s, room_k):
             happiness = calculate_happiness(mm, G)
             if happiness > max_happiness:
                 max_happiness = happiness
                 ret = mm
                 k = room_k
         return
     pp = nodes[0]
     r = 0
     n = copy.deepcopy(nodes)
     n.remove(pp)
     for room in mapping:
         m = copy.deepcopy(mapping)
         m[room] = m[room] + [pp]
         mm = convert_dictionary(m)
         if is_valid_solution(mm, G, s, len(m)):
             create(n, m)
         r += 1
     m = copy.deepcopy(mapping)
     m[r] = [pp]
     create(n, m)
Exemple #2
0
def solve_happy(G, s, prob):
    """
    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!
    best_D_so_far = {}
    best_k_so_far = 1
    best_H_so_far = 0.0
    n = nx.number_of_nodes(G)
    
    for k in range(1, n + 1):
        curr_D = {}
        smax = s/k
        G_happy = G.copy()
        while nx.number_of_nodes(G_happy) > k:  
            i = np.random.geometric(p=prob, size = 1).item(0)
            # sort edges by decreasing happiness
            sorted_happiness = sorted(G_happy.edges(data=True), key=lambda y: (y[2]["happiness"], -y[2]["stress"]), reverse=True)
            #i = random.randint(0, len(sorted_happiness))
            if len(sorted_happiness) == 0:
                break
            #need to merge nodes A and B
            i = i % len(sorted_happiness)
            n1, n2, _ = sorted_happiness[i]
            if G_happy.nodes[n1].get("stress", 0) + G_happy.nodes[n2].get("stress", 0) + G_happy.edges[n1, n2]["stress"] <= smax:
                merge(G_happy, n1, n2)
                
            else:
                G_happy.remove_edge(n1,n2)
                

        if nx.number_of_nodes(G_happy) == k:
            room = 0
            for node in list(G_happy.nodes):
                if isinstance(node, int):
                    temp = [node]
                else:
                    temp = node.split(' ')
                    temp = [int(x) for x in temp]
                curr_D[room] = temp
                room += 1
            curr_D = convert_dictionary(curr_D)
            
        else:
            continue
       
        if is_valid_solution(curr_D, G, s, k):
            if calculate_happiness(curr_D, G) > best_H_so_far:
                best_D_so_far = curr_D
                best_k_so_far = k
                best_H_so_far = calculate_happiness(curr_D, G)
            #
    # pass
    return best_D_so_far, best_k_so_far
Exemple #3
0
def greedy_happystress(G, G_cop, s, rooms):
    dic = {}
    current_room = 0
    while G_cop.nodes and current_room < rooms:
        dic[current_room] = room_maker(G, G_cop, s / rooms)
        current_room += 1
    return convert_dictionary(dic)
Exemple #4
0
def gamble_solve(G, s, num_open_rooms, reps=100, limit = -1):
    """
    Gambles by randomly assigning students to rooms and checking optimality and stress levels
    Works specifically for input #1 type
     - can be modified by changin num_open_rooms to randint
     - can be modified adding checks before h > best_h to check stress_max val per room
    ToDo:
     - Refactor to use methods in utils.py
     - Make sure truly gamble solution
    """

    students = list(G.nodes)
    #num_open_rooms = int(math.ceil(len(students)/2))
    best_h = -1
    best_rooms = []


    count = 0
    while (count < reps):
        rooms = []
        for _ in range(num_open_rooms):
            rooms.append([])
        temp_students = students.copy()



        rooms = random_assign(temp_students, rooms, limit)

        temp_d = utils.convert_list_to_dictionary(rooms)
        dict = utils.convert_dictionary(temp_d)

        valid = utils.is_valid_solution(dict, G, s, num_open_rooms)

        h = utils.calculate_happiness(dict, G)

        if ((valid) and (h > best_h)):
            best_rooms = []
            room_temp = rooms.copy()
            for i in range(len(rooms)):
                best_rooms += [rooms[i].copy()]
            best_h = h
        elif (not valid):
            count = count - 1
        count = count + 1
    return best_rooms.copy(), best_h
Exemple #5
0
def kcluster_beef(G, s):
    best = {}
    best_happiness = 0
    for i in range(1, len(G.nodes)):
        local_best = {}
        j = 0
        valid = 0
        total_combos = []
        while j <= 1000:
            if (not valid) and j >= 100:
                j = 1000
            init_centroids = random.sample(range(0, len(list(G.nodes))), i)
            if (len(total_combos) < comb(len(G.nodes), i)):
                while (init_centroids in total_combos):
                    init_centroids = random.sample(
                        range(0, len(list(G.nodes))), i)
            total_combos.append(init_centroids)
            classes = making_classes_beef(init_centroids, G, s / i)
            for c in classes:
                d = making_dic(c)
                dic = convert_dictionary(d)
                if is_valid_solution(dic, G, s, i):
                    valid = 1
                    local_best[calculate_happiness(dic, G)] = dic
                if len(local_best) != 0:
                    local = max(local_best.keys())
                    if len(best) != 0:
                        if best_happiness < local:
                            best_happiness = local
                            best = local_best[local]
                    else:
                        best_happiness = local
                        best = local_best[local]
            j += 1
    h = calculate_happiness(best, G)
    return best
Exemple #6
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
Exemple #7
0
def gamble_solve_with_greedy(G, s, i, limit=-1, reps=100):

    """
    Gambles by randomly assigning students to rooms and checking optimality and stress levels
    Works specifically for input #1 type
     - can be modified by changin num_open_rooms to randint
     - can be modified adding checks before h > best_h to check stress_max val per room
    Methods to use:
     - random_assign(students, rooms, limit_per_room = -1):

    """

    students = list(G.nodes)
    #num_open_rooms = int(math.ceil(len(students)/2))
    best_h = -1
    best_rooms = []


    count = 0
    while (count < reps):
        rooms = []
        for _ in range(i):
            rooms.append([])
        temp_students = students.copy()

        rooms = random_assign(temp_students, rooms, limit)

        room_to_students = utils.convert_list_to_dictionary(rooms)


        G_copy = nx.Graph(G)
        assigned_students = [i+1 for i in range(50)]
        happy_dict = {}
        happy_edgeList = [] #sorted(G_copy.edges, key=lambda x: G_copy.edges[x]["happiness"], reverse = True)


        happy_edgeList = greedy_solver_2.remove_students_greedy(G, G_copy, s, room_to_students, happy_dict, assigned_students, happy_edgeList)



        #ToDo: Modify greedy_2 to take in optional parameters of rooms, other stuff thats initialized @ start
        room_to_students = greedy_solver_2.greedy_solve_2(G, s, assigned_students, room_to_students, happy_edgeList)


        dict = utils.convert_dictionary(room_to_students)


        valid = utils.is_valid_solution(dict, G, s, num_open_rooms)

        h = utils.calculate_happiness(dict, G)

        if ((valid) and (h > best_h)):
            best_rooms = []
            room_temp = rooms.copy()
            for i in range(len(rooms)):
                best_rooms += [rooms[i].copy()]
            best_h = h
        #elif (not valid):
            #count = count - 1
        count = count + 1
    return best_rooms.copy(), best_h
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
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
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
    """
    # we are originally starting k off as 1, we will continually add groups until we have a valid assignment
    num_groups = 1
    stress_limit = s / num_groups
    curr_room = 0

    room_to_student = {0: []}
    students_left = []

    # initially, all students are still left unmatched
    num_students = G.order()
    for i in range(num_students):
        students_left.append(i)

    while students_left:
        room = room_to_student[curr_room]
        if not room:
            # current room is empty
            best_pair = find_best_pair(G, students_left, stress_limit)
            if best_pair == -1:
                # there exist no pairs that satisfy the current stress limit, cannot form any more groups
                for student in students_left:
                    room_to_student[curr_room] = [student]
                    students_left.remove(student)
                    curr_room += 1
                num_groups = len(room_to_student)
                curr_room = num_groups - 1
            else:
                # there exists a best pair that satisfies the current stress limit
                students_left.remove(best_pair[0])
                students_left.remove(best_pair[1])
                room.append(best_pair[0])
                room.append(best_pair[1])
        else:
            # current room is not empty
            new_member = find_best_addition(G, students_left, stress_limit,
                                            room)
            if new_member == -1:
                # we cannot add anyone to the current group
                num_groups += 1
                stress_limit = s / num_groups
                curr_room += 1
                for i in range(len(room_to_student)):
                    trim_group(G, students_left, stress_limit,
                               room_to_student[i])
                room_to_student[curr_room] = []

            else:
                # we can add someone to the current group
                room.append(new_member)
                students_left.remove(new_member)

    return convert_dictionary(room_to_student), curr_room + 1, room_to_student