def hopkroft(graph, structure): transformed_graph = {} supervisors = graph.getEdges() students = graph.getStuEdges() correspondence = {} sup_name = 0 list_supervisors = [supervisor for supervisor in supervisors] list_students = [student for student in students] random.shuffle(list_supervisors) random.shuffle(list_students) a_to_b = {} for i in range(len(list_students)): a_to_b[list_students[i]] = 'stu' + str(i) for supervisor in list_supervisors: cardinality = structure[supervisor] for i in range(cardinality): for stu in supervisors[supervisor]: transformed_graph.setdefault(sup_name, set()).add(a_to_b[stu]) correspondence[sup_name] = supervisor sup_name = sup_name + 1 m = HopcroftKarp(transformed_graph).maximum_matching() result = BipartiteGraph() for student in students: sup_code = m[a_to_b[student]] result.addEdge(correspondence[sup_code], student) return result
def test_case3(self): """Testing addEdge function when adding an edge that already exists""" graph = BipartiteGraph() graph.addEdge("supervisor1", "student1") graph.addEdge("supervisor1", "student1") val1 = graph.getSupervisorDegree("supervisor1") val2 = graph.getStudentDegree("student1") self.assertEqual((val1, val2), (1, 1))
def test_case6(self): """Testing the removeEdge function in a graph with existing nodes and edges""" graph = BipartiteGraph() graph.addEdge("supervisor1", "student1") graph.removeEdge("supervisor1", "student1") val1 = graph.getStudentDegree("student1") val2 = graph.getSupervisorDegree("supervisor1") self.assertEqual((val1, val2), (0, 0))
def test_case20(self): """Testing the isValid function - when number of students less than actual""" graph = BipartiteGraph() graph.addEdge("supervisor1","student1") graph.addEdge("supervisor3","student3") graph.addEdge("supervisor2","student2") solution = Solution(graph) result = solution.isValid(self.students,self.supervisors) self.assertFalse(result)
def test_case1(self): """Testing addEdge function in an empty graph""" graph = BipartiteGraph() graph.addEdge("supervisor1", "student1") val1 = graph.getStudents("supervisor1") val2 = graph.getSupervisors("student1") expected1 = ["student1"] expected2 = ["supervisor1"] self.assertEqual((val1, val2), (expected1, expected2))
def uniform(solution1, solution2, supervisors, students, k=None): """ An implementation of uniform crossover """ graph1 = solution1.getGraph() graph2 = solution2.getGraph() stuEdges1 = graph1.getStuEdges() stuEdges2 = graph2.getStuEdges() g = BipartiteGraph() for stu in students: if random.random() < 0.5: sup = stuEdges1[stu][0] else: sup = stuEdges2[stu][0] g.addEdge(sup, stu) fixSolution(g, supervisors, students) return Solution(g)
def test_case2(self): """Testing addEdge function in a graph with existing nodes and edges""" graph = BipartiteGraph() graph.addEdge("supervisor1", "student1") graph.addEdge("supervisor2", "student4") graph.addEdge("supervisor3", "student3") val1 = graph.getSupervisorDegree("supervisor1") graph.addEdge("supervisor1", "student2") curr = graph.getSupervisorDegree("supervisor1") val2 = graph.getSupervisors("student2") expected2 = ["supervisor1"] self.assertEqual((curr - 1, expected2), (val1, val2))
class MutationTest(unittest.TestCase): def setUp(self): #Creating a list of students and supervisors topicNames, topicPaths, topicIDs, levels = parseFile( "pystsup/test/acm.txt") self.supervisors = {} self.students = {} stuID = "student" supID = "supervisor" stuList1 = [ "MapReduce-based systems", "Multidimensional range search", "Open source software", "Data mining", "Online shopping" ] stuList2 = [ "Heuristic function construction", "Multi-agent systems", "Open source software", "Data mining", "Speech recognition" ] stuList3 = [ "Multi-agent systems", "Speech recognition", "Heuristic function construction", "Data mining", "Object identification" ] stuList4 = [ "Multi-agent systems", "Intelligent agents", "Speech recognition", "Object identification", "Heuristic function construction" ] supList1 = [ "Multi-agent systems", "Intelligent agents", "MapReduce-based systems", "Object identification", "Heuristic function construction" ] supList2 = [ "Open source software", "Data mining", "Speech recognition", "Object identification", "Heuristic function construction" ] supList3 = [ "Multi-agent systems", "Intelligent agents", "Speech recognition", "Object identification", "Heuristic function construction" ] supList = [supList1, supList2, supList3] supQuota = [2, 2, 1] stuList = [stuList1, stuList2, stuList3, stuList4] for i in range(3): toAdd = [] sup_list = {} rank = 0 for kw in supList[i]: rank += 1 sup_list[rank] = [ kw, getPath(kw, topicNames, topicPaths, topicIDs) ] sup = supID + str(i + 1) quota = supQuota[i] supervisorObject = Supervisor(sup, sup_list, quota) self.supervisors[sup] = supervisorObject for i in range(4): toAdd = [] stu_list = {} rank = 0 for kw in stuList[i]: rank += 1 stu_list[rank] = [ kw, getPath(kw, topicNames, topicPaths, topicIDs) ] stu = stuID + str(i + 1) studentObject = Student(stu, stu_list) self.students[stu] = studentObject # Generating rank weights for c = 0.5 self.rankWeights = Solution.calcRankWeights() #Creating fitness cache self.dummySolution = Solution() self.fitnessCache = {} for sup in self.supervisors: supervisorKeywords = self.supervisors[sup].getKeywords() for stu in self.students: studentKeywords = self.students[stu].getKeywords() f_stu, f_sup = self.dummySolution.kw_similarity( studentKeywords, supervisorKeywords, self.rankWeights) self.fitnessCache[str((stu, sup))] = (f_stu, f_sup) # Creating two graphs and solutions self.graph1 = BipartiteGraph() self.graph2 = BipartiteGraph() self.graph1.addEdge("supervisor1", "student2") self.graph1.addEdge("supervisor2", "student4") self.graph1.addEdge("supervisor3", "student1") self.graph1.addEdge("supervisor1", "student3") self.solution1 = Solution(self.graph1) self.graph2.addEdge("supervisor1", "student2") self.graph2.addEdge("supervisor2", "student1") self.graph2.addEdge("supervisor3", "student4") self.graph2.addEdge("supervisor1", "student3") self.solution2 = Solution(self.graph2) def test_case1(self): """Testing mutation function for solution 1""" mutationProbability = 0.3 swapProbability = 0.5 transferProbability = 0.5 solution3 = mutate(self.solution1, self.supervisors, mutationProbability, swapProbability, transferProbability) result = solution3.isValid(self.students, self.supervisors) self.assertTrue(result) def test_case2(self): """Testing mutation function for solution 1 - only swap operation""" mutationProbability = 0.3 swapProbability = 0.5 transferProbability = 0 solution3 = mutate(self.solution1, self.supervisors, mutationProbability, swapProbability, transferProbability) result = solution3.isValid(self.students, self.supervisors) self.assertTrue(result) def test_case3(self): """Testing mutation function for solution 1 - only transfer operation""" mutationProbability = 0.3 swapProbability = 0 transferProbability = 0.5 solution3 = mutate(self.solution1, self.supervisors, mutationProbability, swapProbability, transferProbability) result = solution3.isValid(self.students, self.supervisors) self.assertTrue(result) def test_case4(self): """Testing mutation function for solution 2""" mutationProbability = 0.3 swapProbability = 0.5 transferProbability = 0.3 solution4 = mutate(self.solution2, self.supervisors, mutationProbability, swapProbability, transferProbability) result = solution4.isValid(self.students, self.supervisors) self.assertTrue(result) def test_case5(self): """Testing mutation function for a randomly created solution 1""" mutationProbability = 0.3 swapProbability = 0.2 transferProbability = 0.8 solution1 = Solution.generateRandomSolution(self.students, self.supervisors) solution2 = mutate(solution1, self.supervisors, mutationProbability, swapProbability, transferProbability) result = solution2.isValid(self.students, self.supervisors) self.assertTrue(result) def test_case6(self): """Testing mutation function for a randomly created solution 2""" mutationProbability = 0.3 swapProbability = 0.7 transferProbability = 0 solution1 = Solution.generateRandomSolution(self.students, self.supervisors) solution2 = mutate(solution1, self.supervisors, mutationProbability, swapProbability, transferProbability) result = solution2.isValid(self.students, self.supervisors) self.assertTrue(result)
class CrossoverTest(unittest.TestCase): def setUp(self): #Creating a list of students and supervisors topicNames, topicPaths, topicIDs, levels = parseFile( "pystsup/test/acm.txt") self.supervisors = {} self.students = {} stuID = "student" supID = "supervisor" stuList1 = [ "MapReduce-based systems", "Multidimensional range search", "Open source software", "Data mining", "Online shopping" ] stuList2 = [ "Heuristic function construction", "Multi-agent systems", "Open source software", "Data mining", "Speech recognition" ] stuList3 = [ "Multi-agent systems", "Speech recognition", "Heuristic function construction", "Data mining", "Object identification" ] stuList4 = [ "Multi-agent systems", "Intelligent agents", "Speech recognition", "Object identification", "Heuristic function construction" ] supList1 = [ "Multi-agent systems", "Intelligent agents", "MapReduce-based systems", "Object identification", "Heuristic function construction" ] supList2 = [ "Open source software", "Data mining", "Speech recognition", "Object identification", "Heuristic function construction" ] supList3 = [ "Multi-agent systems", "Intelligent agents", "Speech recognition", "Object identification", "Heuristic function construction" ] supList = [supList1, supList2, supList3] supQuota = [2, 2, 1] stuList = [stuList1, stuList2, stuList3, stuList4] for i in range(3): toAdd = [] sup_list = {} rank = 0 for kw in supList[i]: rank += 1 sup_list[rank] = [ kw, getPath(kw, topicNames, topicPaths, topicIDs) ] sup = supID + str(i + 1) quota = supQuota[i] supervisorObject = Supervisor(sup, sup_list, quota) self.supervisors[sup] = supervisorObject for i in range(4): toAdd = [] stu_list = {} rank = 0 for kw in stuList[i]: rank += 1 stu_list[rank] = [ kw, getPath(kw, topicNames, topicPaths, topicIDs) ] stu = stuID + str(i + 1) studentObject = Student(stu, stu_list) self.students[stu] = studentObject # Generating rank weights for c = 0.5 self.rankWeights = Solution.calcRankWeights() #Creating fitness cache self.dummySolution = Solution() self.fitnessCache = {} for sup in self.supervisors: supervisorKeywords = self.supervisors[sup].getKeywords() for stu in self.students: studentKeywords = self.students[stu].getKeywords() f_stu, f_sup = self.dummySolution.kw_similarity( studentKeywords, supervisorKeywords, self.rankWeights) self.fitnessCache[str((stu, sup))] = (f_stu, f_sup) # Creating two graphs and solutions self.graph1 = BipartiteGraph() self.graph2 = BipartiteGraph() self.graph1.addEdge("supervisor1", "student2") self.graph1.addEdge("supervisor2", "student4") self.graph1.addEdge("supervisor3", "student1") self.graph1.addEdge("supervisor1", "student3") self.solution1 = Solution(self.graph1) self.graph2.addEdge("supervisor1", "student2") self.graph2.addEdge("supervisor2", "student1") self.graph2.addEdge("supervisor3", "student4") self.graph2.addEdge("supervisor1", "student3") self.solution2 = Solution(self.graph2) # Creating test for simplify function self.graph3 = BipartiteGraph() self.graph3.addEdge("s1", "st1") self.graph3.addEdge("s1", "st4") self.graph3.addEdge("s2", "st1") self.graph3.addEdge("s2", "st2") self.graph3.addEdge("s3", "st3") self.graph3.addEdge("s3", "st5") self.graph3.addEdge("s4", "st3") self.graph3.addEdge("s4", "st5") self.structure3 = {"s1": 2, "s2": 1, "s3": 1, "s4": 1} self.graph4 = BipartiteGraph() self.graph4.addEdge("s1", "st1") self.graph4.addEdge("s1", "st3") self.graph4.addEdge("s1", "st4") self.graph4.addEdge("s2", "st3") self.graph4.addEdge("s2", "st4") self.graph4.addEdge("s3", "st1") self.graph4.addEdge("s3", "st2") self.structure4 = {"s1": 1, "s2": 2, "s3": 1} #Creating 60-400 data set self.students2, self.supervisors2 = createRandomData(60, 400, 405) # m,n,quotaSum,level=3,maxQuota=10,minQuota=4,no_topics=5 self.students3, self.supervisors3 = createRandomData(3, 5, 8, minQuota=1, maxQuota=3) self.problem_instances = [ #createRandomData(6,10,16,minQuota=1) createRandomData(60, 400, 405) ] #def test_case_special_cr(self): #for (students,supervisors) in self.problem_instances : #for i in range(100): # sol1 = Solution.generateRandomSolution(students,supervisors) # sol2 = Solution.generateRandomSolution(students,supervisors) # sp_crossover(sol1,sol2) def test_case_simplify1(self): response, already_set = simplify(self.graph3, self.structure3) self.assertTrue(("s1", "st1") in response) self.assertTrue(("s1", "st4") in response) self.assertTrue(("s2", "st2") in response) self.assertEqual(self.structure3["s1"], 0) self.assertEqual(self.structure3["s2"], 0) self.assertEqual(self.structure3["s3"], 1) self.assertEqual(self.structure3["s4"], 1) self.assertTrue(("stu", "st1") in already_set) self.assertTrue(("stu", "st4") in already_set) self.assertTrue(("stu", "st2") in already_set) self.assertTrue(("sup", "s1") in already_set) self.assertTrue(("sup", "s2") in already_set) self.assertEqual(len(response), 3) def test_case_simplify2(self): response, already_set = simplify(self.graph4, self.structure4) self.assertTrue(("s1", "st1") in response) self.assertTrue(("s2", "st3") in response) self.assertTrue(("s2", "st4") in response) self.assertTrue(("s3", "st2") in response) self.assertEqual(self.structure4["s1"], 0) self.assertEqual(self.structure4["s2"], 0) self.assertEqual(self.structure4["s3"], 0) self.assertTrue(("stu", "st1") in already_set) self.assertTrue(("stu", "st3") in already_set) self.assertTrue(("stu", "st4") in already_set) self.assertTrue(("stu", "st2") in already_set) self.assertTrue(("sup", "s1") in already_set) self.assertTrue(("sup", "s2") in already_set) self.assertTrue(("sup", "s3") in already_set) self.assertEqual(len(response), 4) def test_case1(self): """Testing the ERX Modified Crossover function - for 3sup:4stu solution""" solution3 = crossover(self.solution1, self.solution2, self.supervisors, self.students) structureResult = (solution3.getGraph().getStructure() == self.solution1.getGraph().getStructure()) or ( solution3.getGraph().getStructure() == self.solution2.getGraph().getStructure()) result = (solution3.isValid(self.students, self.supervisors)) and (structureResult) self.assertTrue(result) def test_case2(self): """Testing the kPoint crossover function - for 3sup:4stu solution""" solution3 = kPoint(self.solution1, self.solution2, self.supervisors, self.students, k=3) result = solution3.isValid(self.students, self.supervisors) self.assertTrue(result) def test_case3(self): """Testing the EDX Modified crossover function - when one valid and one dummy solution are passed""" self.assertRaises( KeyError, lambda: crossover(self.solution1, self.dummySolution, self.supervisors, self.students)) def test_case4(self): """Testing the K Point Crossover function - when one valid and one dummy solution are passed""" self.assertRaises( KeyError, lambda: kPoint(self.solution2, self.dummySolution, self.supervisors, self.students, k=3)) def test_case5(self): """Testing the ERX Modified Crossover function - for random 3sup:4stu solution""" solution1 = Solution.generateRandomSolution(self.students, self.supervisors) solution2 = Solution.generateRandomSolution(self.students, self.supervisors) solution3 = crossover(solution1, solution2, self.supervisors, self.students) structureResult = (solution3.getGraph().getStructure() == solution1.getGraph().getStructure()) or ( solution3.getGraph().getStructure() == solution2.getGraph().getStructure()) result = (solution3.isValid(self.students, self.supervisors)) and (structureResult) self.assertTrue(result) def test_case6(self): """Testing the K Point Crossover function - for random 3sup:4stu solution""" solution1 = Solution.generateRandomSolution(self.students, self.supervisors) solution2 = Solution.generateRandomSolution(self.students, self.supervisors) solution3 = kPoint(solution1, solution2, self.supervisors, self.students, k=3) result = solution3.isValid(self.students, self.supervisors) self.assertTrue(result) def test_case7(self): """Testing the K Point crossover function - when k=0""" solution3 = kPoint(self.solution1, self.solution2, self.supervisors, self.students, k=0) structureResult = (solution3.getGraph().getStructure() == self.solution1.getGraph().getStructure()) or ( solution3.getGraph().getStructure() == self.solution2.getGraph().getStructure()) result = (solution3.isValid(self.students, self.supervisors)) and (structureResult) self.assertTrue(result) def test_case8(self): """Testing the K Point crossover function - when k=1""" solution3 = kPoint(self.solution1, self.solution2, self.supervisors, self.students, k=1) result = solution3.isValid(self.students, self.supervisors) self.assertTrue(result) def test_case9(self): """Testing the K Point crossover function - when k > no_students""" self.assertRaises( ValueError, lambda: kPoint(self.solution1, self.solution2, self.supervisors, self.students, k=6)) def test_case10(self): """Testing the ERX Modified crossover function - for random 60sup:400stu solution""" solution1 = Solution.generateRandomSolution(self.students2, self.supervisors2) solution2 = Solution.generateRandomSolution(self.students2, self.supervisors2) solution3 = crossover(solution1, solution2, self.supervisors2, self.students2) structureResult = (solution3.getGraph().getStructure() == solution1.getGraph().getStructure()) or ( solution3.getGraph().getStructure() == solution2.getGraph().getStructure()) result = (solution3.isValid(self.students2, self.supervisors2)) and (structureResult) self.assertTrue(result) def test_case11(self): """Testing the ERX Modified crossover function - for random 60sup:400stu solution""" solution1 = Solution.generateRandomSolution(self.students2, self.supervisors2) solution2 = Solution.generateRandomSolution(self.students2, self.supervisors2) solution3 = crossover(solution1, solution2, self.supervisors, self.supervisors2, self.students2) structureResult = (solution3.getGraph().getStructure() == solution1.getGraph().getStructure()) or ( solution3.getGraph().getStructure() == solution2.getGraph().getStructure()) result = (solution3.isValid(self.students2, self.supervisors2)) and (structureResult) self.assertTrue(result) def test_case12(self): """Testing the kPoint crossover function - for random 60sup:400stu solution""" solution1 = Solution.generateRandomSolution(self.students2, self.supervisors2) solution2 = Solution.generateRandomSolution(self.students2, self.supervisors2) solution3 = kPoint(solution1, solution2, self.supervisors2, self.students2, k=15) result = solution3.isValid(self.students2, self.supervisors2) self.assertTrue(result) def test_case13(self): """Testing the kPoint crossover function - for random 60sup:400stu solution""" solution1 = Solution.generateRandomSolution(self.students2, self.supervisors2) solution2 = Solution.generateRandomSolution(self.students2, self.supervisors2) solution3 = kPoint(solution1, solution2, self.supervisors2, self.students2, k=15) result = solution3.isValid(self.students2, self.supervisors2) self.assertTrue(result)
class SolutionTest(unittest.TestCase): def setUp(self): #Creating a list of students and supervisors topicNames,topicPaths, topicIDs, levels = parseFile("pystsup/test/acm.txt") self.supervisors = {} self.students = {} stuID = "student" supID = "supervisor" stuList1 = ["MapReduce-based systems","Multidimensional range search","Open source software","Data mining","Online shopping"] stuList2 = ["Heuristic function construction","Multi-agent systems","Open source software","Data mining","Speech recognition"] stuList3 = ["Multi-agent systems","Speech recognition","Heuristic function construction","Data mining","Object identification"] stuList4 = ["Multi-agent systems","Intelligent agents","Speech recognition","Object identification","Heuristic function construction"] supList1 = ["Multi-agent systems","Intelligent agents","MapReduce-based systems","Object identification","Heuristic function construction"] supList2 = ["Open source software","Data mining","Speech recognition","Object identification","Heuristic function construction"] supList3 = ["Multi-agent systems","Intelligent agents","Speech recognition","Object identification","Heuristic function construction"] supList = [supList1,supList2,supList3] supQuota = [2,2,1] stuList = [stuList1,stuList2,stuList3,stuList4] for i in range(3): toAdd = [] sup_list = {} rank = 0 for kw in supList[i]: rank+=1 sup_list[rank]=[kw,getPath(kw,topicNames,topicPaths, topicIDs)] sup = supID + str(i+1) quota = supQuota[i] supervisorObject = Supervisor(sup,sup_list,quota) self.supervisors[sup]=supervisorObject for i in range(4): toAdd = [] stu_list = {} rank = 0 for kw in stuList[i]: rank+=1 stu_list[rank] = [kw,getPath(kw,topicNames,topicPaths, topicIDs)] stu = stuID + str(i+1) studentObject = Student(stu,stu_list) self.students[stu]=studentObject # Generating rank weights for c = 0.5 self.rankWeights = Solution.calcRankWeights() #Creating fitness cache self.dummySolution = Solution() self.fitnessCache = {} for sup in self.supervisors: supervisorKeywords = self.supervisors[sup].getKeywords() for stu in self.students: studentKeywords = self.students[stu].getKeywords() f_stu,f_sup = self.dummySolution.kw_similarity(studentKeywords,supervisorKeywords, self.rankWeights) self.fitnessCache[str((stu,sup))] = (f_stu,f_sup) # Creating two graphs and solutions self.graph1 = BipartiteGraph() self.graph2 = BipartiteGraph() self.graph1.addEdge("supervisor1","student2") self.graph1.addEdge("supervisor2","student4") self.graph1.addEdge("supervisor3","student1") self.graph1.addEdge("supervisor1","student3") self.solution1 = Solution(self.graph1) self.graph2.addEdge("supervisor1","student2") self.graph2.addEdge("supervisor2","student1") self.graph2.addEdge("supervisor3","student4") self.graph2.addEdge("supervisor1","student3") self.solution2 = Solution(self.graph2) def test_case1(self): """Testing the create random solution function""" solution1 = Solution.generateRandomSolution(self.students,self.supervisors) result = solution1.isValid(self.students,self.supervisors) self.assertEqual(result,True) def test_case2(self): """Testing the calculate Rank Weights function when c = 1""" rankWeights = Solution.calcRankWeights(c=1) result = True expected = 0.2 for i in rankWeights: if rankWeights[i] != expected: result = False break self.assertTrue(result) def test_case3(self): """Testing the calculate rank weights function when c = 0.3""" rankWeights = Solution.calcRankWeights(c=0.3) result = round(sum(rankWeights.values()),15) expected = {1: 0.701705143, 2: 0.210511543, 3: 0.063153463, 4: 0.0189460389, 5: 0.005683812} result2 = True for i in rankWeights: if round(rankWeights[i],8) != round(expected[i],8): result2 = False break self.assertEqual((result,result2),(1,True)) def test_case4(self): """Testing the calculate rank weights function when c=0 and n=0""" rankWeights = Solution.calcRankWeights(c=0,n=0) expected = {} self.assertEqual(rankWeights,expected) def test_case5(self): """Testing similarity function with 2 non-similar profiles (0)""" dummySolution = Solution() supervisorKeywords = self.supervisors["supervisor3"].getKeywords() studentKeywords = self.students["student1"].getKeywords() f_stu,f_sup = dummySolution.kw_similarity(studentKeywords,supervisorKeywords,self.rankWeights) expected_fstu = 0.1217742 expected_fsup = 0.1416129 expected = (round(expected_fstu,6),round(expected_fsup,6)) retrieved = (round(f_stu,6),round(f_sup,6)) self.assertEqual(retrieved,expected) def test_case6(self): """Testing similarity function with 2 identical profiles (1)""" dummySolution = Solution() supervisorKeywords = self.supervisors["supervisor3"].getKeywords() studentKeywords = self.students["student4"].getKeywords() f_stu,f_sup = dummySolution.kw_similarity(studentKeywords,supervisorKeywords,self.rankWeights) self.assertEqual((f_stu,f_sup),(1,1)) def test_case7(self): """Testing similarity function between 2 mostly similar profiles """ dummySolution = Solution() supervisorKeywords = self.supervisors["supervisor3"].getKeywords() studentKeywords = self.students["student3"].getKeywords() f_stu,f_sup = dummySolution.kw_similarity(studentKeywords,supervisorKeywords,self.rankWeights) expected_fstu = 0.708333 expected_fsup = 0.726882 expected = (round(expected_fstu,6),round(expected_fsup,6)) retrieved = (round(f_stu,6),round(f_sup,6)) self.assertEqual(retrieved,expected) def test_case8(self): """Testing similarity function between 2 less similar profiles """ dummySolution = Solution() supervisorKeywords = self.supervisors["supervisor3"].getKeywords() studentKeywords = self.students["student2"].getKeywords() f_stu,f_sup = dummySolution.kw_similarity(studentKeywords,supervisorKeywords,self.rankWeights) expected_fstu = 0.255645 expected_fsup = 0.522043 expected = (round(expected_fstu,6),round(expected_fsup,6)) retrieved = (round(f_stu,6),round(f_sup,6)) self.assertEqual(retrieved,expected) def test_case9(self): """Testing intersection function - between 2 non-intersecting lists""" list1 = self.supervisors["supervisor1"].getKeywords()[1][1] list2 = self.students["student1"].getKeywords()[1][1] result = self.dummySolution._intersection(list1,list2) self.assertEqual(result,1) def test_case10(self): """Testing intersection function - between 2 lists with 1 common element""" list1 = ["Document types","General and reference"] list2 = ["Cross-computing tools and techniques","General and reference"] result = self.dummySolution._intersection(list1,list2) self.assertEqual(result,1) def test_case11(self): """Testing intersection function - between 2 lists with 2 common element""" list1 = ["Reference works","Document types","General and reference"] list2 = ["Surveys and overviews","Document types","General and reference"] result = self.dummySolution._intersection(list1,list2) self.assertEqual(result,2) def test_case12(self): """Testing intersection function - between 2 lists with 3 common element""" list1 = ["Digital signal processing","Signal processing systems","Communication hardware, interfaces and storage","Hardware"] list2 = ["Beamforming","Signal processing systems","Communication hardware, interfaces and storage","Hardware"] result = self.dummySolution._intersection(list1,list2) self.assertEqual(result,3) def test_case13(self): """Testing intersection function - between 2 identical lists""" list1 = self.supervisors["supervisor1"].getKeywords()[1][1] list2 = self.students["student4"].getKeywords()[1][1] result = self.dummySolution._intersection(list1,list2) self.assertEqual(result,5) def test_case14(self): """Testing the calc fitness function for a bad solution""" self.solution1.calcFitness(self.students,self.supervisors,self.rankWeights,self.fitnessCache) F_st = self.solution1.getFst() F_sup = self.solution1.getFsup() retrieved = (round(F_sup,6),round(F_st,6)) expected = (0.600813,1.457903) self.assertEqual(retrieved,expected) def test_case15(self): """Testing the calc fitness function for a fairly good solution""" self.solution2.calcFitness(self.students,self.supervisors,self.rankWeights,self.fitnessCache) F_st = self.solution2.getFst() F_sup = self.solution2.getFsup() retrieved = (round(F_sup,7),round(F_st,7)) expected = (1.1482502,2.1678495) self.assertEqual(retrieved,expected) def test_case16(self): """Testing transfer function - a valid transfer""" self.solution1.transferStudent("student3","supervisor1","supervisor2",self.supervisors) solutionGraph = self.solution1.getGraph() val1 = solutionGraph.getStudents("supervisor1") val2 = solutionGraph.getStudents("supervisor2") val3 = solutionGraph.getSupervisors("student3") expected = (['student2'],['student4','student3'],['supervisor2']) self.assertEqual((val1,val2,val3),expected) def test_case17(self): """Testing getGraph function""" solutionGraph = self.solution1.getGraph() val1 = isinstance(solutionGraph,BipartiteGraph) val2 = solutionGraph.getStuEdges() result = (val1,val2) expected1 = True expected2 = {'student1':['supervisor3'],'student2':['supervisor1'],'student3':['supervisor1'],'student4':['supervisor2']} expected = (expected1,expected2) self.assertEqual(result,expected) def test_case18(self): """Testing the isValid function - passing a null graph""" graph = BipartiteGraph() solution = Solution(graph) result = solution.isValid(self.students,self.supervisors) self.assertFalse(result) def test_case19(self): """Testing the isValid function - when number of supervisors less than actual""" graph = BipartiteGraph() graph.addEdge("supervisor1","student1") graph.addEdge("supervisor2","student4") graph.addEdge("supervisor2","student3") graph.addEdge("supervisor1","student2") solution = Solution(graph) result = solution.isValid(self.students,self.supervisors) self.assertFalse(result) def test_case20(self): """Testing the isValid function - when number of students less than actual""" graph = BipartiteGraph() graph.addEdge("supervisor1","student1") graph.addEdge("supervisor3","student3") graph.addEdge("supervisor2","student2") solution = Solution(graph) result = solution.isValid(self.students,self.supervisors) self.assertFalse(result) def test_case21(self): """Testing the isValid function - when a supervisor's allocation exceeds their quota""" self.graph1.addEdge("supervisor3","student2") solution = Solution(self.graph1) result = solution.isValid(self.students,self.supervisors) self.assertFalse(result) def test_case22(self): """Testing the isValid function - when a supervisor's degree is 0""" self.graph1.removeEdge("supervisor3","student1") solution = Solution(self.graph1) result = solution.isValid(self.students,self.supervisors) self.assertFalse(result) def test_case23(self): """Testing the isValid function - when a student's degree is not equal to 1""" self.graph1.addEdge("supervisor2","student3") solution = Solution(self.graph1) result = solution.isValid(self.students,self.supervisors) self.assertFalse(result) def test_case24(self): """Testing is valid function - when a correct graph is passed""" result1 = self.solution1.isValid(self.students,self.supervisors) result2 = self.solution2.isValid(self.students,self.supervisors) self.assertEqual((result1,result2),(True,True)) def test_case25(self): """Testing 'dominates' function - when both Fst and Fsup are greater in solution1 than solution2""" solution1 = Solution() solution2 = Solution() solution1.setFst(29.5) solution1.setFsup(0.4) solution2.setFst(27.5) solution2.setFsup(0.2) result = solution1.dominates(solution2) self.assertTrue(result) def test_case26(self): """Testing 'dominates' function - when only Fsup is greater in solution2 than solution1""" solution1 = Solution() solution2 = Solution() solution1.setFst(29.5) solution1.setFsup(0.4) solution2.setFst(27.5) solution2.setFsup(0.6) result = solution1.dominates(solution2) self.assertFalse(result) def test_case27(self): """Testing 'dominates' function - when Fsup is same in solution2 and solution1""" solution1 = Solution() solution2 = Solution() solution1.setFst(29.5) solution1.setFsup(0.4) solution2.setFst(27.5) solution2.setFsup(0.4) result = solution1.dominates(solution2) self.assertTrue(result) def test_case28(self): """Testing 'dominates' function - when only Fst is greater in solution2 and solution1""" solution1 = Solution() solution2 = Solution() solution1.setFst(26.5) solution1.setFsup(0.4) solution2.setFst(27.5) solution2.setFsup(0.2) result = solution1.dominates(solution2) self.assertFalse(result) def test_case29(self): """Testing 'dominates' function - when both Fst and Fsup are lesser in solution1 than solution2""" solution1 = Solution() solution2 = Solution() solution1.setFst(26.5) solution1.setFsup(0.2) solution2.setFst(27.5) solution2.setFsup(0.4) result = solution1.dominates(solution2) self.assertFalse(result) def test_case30(self): """Testing 'dominates' function - when Fst is same in solution2 and solution1""" solution1 = Solution() solution2 = Solution() solution1.setFst(29.5) solution1.setFsup(0.6) solution2.setFst(29.5) solution2.setFsup(0.4) result = solution1.dominates(solution2) self.assertTrue(result) def test_case31(self): """Testing 'dominates' function - when Fst is same in solution2 and solution1""" solution1 = Solution() solution2 = Solution() solution1.setFst(29.5) solution1.setFsup(0.2) solution2.setFst(29.5) solution2.setFsup(0.4) result = solution1.dominates(solution2) self.assertFalse(result) def test_case32(self): """Testing 'dominates' function - when Fst and Fsup are same in solution2 and solution1""" solution1 = Solution() solution2 = Solution() solution1.setFst(29.5) solution1.setFsup(0.4) solution2.setFst(29.5) solution2.setFsup(0.4) result = solution1.dominates(solution2) self.assertFalse(result)
class BipartiteGraphTest(unittest.TestCase): def setUp(self): self.graph1 = BipartiteGraph() self.graph2 = BipartiteGraph() self.graph1.addEdge("supervisor1", "student1") self.graph1.addEdge("supervisor2", "student4") self.graph1.addEdge("supervisor3", "student3") self.graph1.addEdge("supervisor1", "student2") self.graph2.addEdge("supervisor1", "student4") self.graph2.addEdge("supervisor2", "student1") self.graph2.addEdge("supervisor2", "student3") self.graph2.addEdge("supervisor3", "student2") #Creating a list of students and supervisors topicNames, topicPaths, topicIDs, levels = parseFile( "pystsup/test/acm.txt") self.supervisors = {} self.students = {} stuID = "student" supID = "supervisor" stuList1 = [ "MapReduce-based systems", "Multidimensional range search", "Open source software", "Data mining", "Online shopping" ] stuList2 = [ "Heuristic function construction", "Multi-agent systems", "Open source software", "Data mining", "Speech recognition" ] stuList3 = [ "Multi-agent systems", "Speech recognition", "Heuristic function construction", "Data mining", "Object identification" ] stuList4 = [ "Multi-agent systems", "Intelligent agents", "Speech recognition", "Object identification", "Heuristic function construction" ] supList1 = [ "Multi-agent systems", "Intelligent agents", "MapReduce-based systems", "Object identification", "Heuristic function construction" ] supList2 = [ "Open source software", "Data mining", "Speech recognition", "Object identification", "Heuristic function construction" ] supList3 = [ "Multi-agent systems", "Intelligent agents", "Speech recognition", "Object identification", "Heuristic function construction" ] supList = [supList1, supList2, supList3] supQuota = [2, 2, 1] stuList = [stuList1, stuList2, stuList3, stuList4] for i in range(3): toAdd = [] sup_list = {} rank = 0 for kw in supList[i]: rank += 1 sup_list[kw] = [ rank, getPath(kw, topicNames, topicPaths, topicIDs) ] sup = supID + str(i + 1) quota = supQuota[i] supervisorObject = Supervisor(sup, sup_list, quota) self.supervisors[sup] = supervisorObject for i in range(4): toAdd = [] stu_list = {} rank = 0 for kw in stuList[i]: rank += 1 stu_list[kw] = [ rank, getPath(kw, topicNames, topicPaths, topicIDs) ] stu = stuID + str(i + 1) studentObject = Student(stu, stu_list) self.students[stu] = studentObject def test_case1(self): """Testing addEdge function in an empty graph""" graph = BipartiteGraph() graph.addEdge("supervisor1", "student1") val1 = graph.getStudents("supervisor1") val2 = graph.getSupervisors("student1") expected1 = ["student1"] expected2 = ["supervisor1"] self.assertEqual((val1, val2), (expected1, expected2)) def test_case2(self): """Testing addEdge function in a graph with existing nodes and edges""" graph = BipartiteGraph() graph.addEdge("supervisor1", "student1") graph.addEdge("supervisor2", "student4") graph.addEdge("supervisor3", "student3") val1 = graph.getSupervisorDegree("supervisor1") graph.addEdge("supervisor1", "student2") curr = graph.getSupervisorDegree("supervisor1") val2 = graph.getSupervisors("student2") expected2 = ["supervisor1"] self.assertEqual((curr - 1, expected2), (val1, val2)) def test_case3(self): """Testing addEdge function when adding an edge that already exists""" graph = BipartiteGraph() graph.addEdge("supervisor1", "student1") graph.addEdge("supervisor1", "student1") val1 = graph.getSupervisorDegree("supervisor1") val2 = graph.getStudentDegree("student1") self.assertEqual((val1, val2), (1, 1)) def test_case4(self): """Testing the remove edge function in an empty graph""" graph = BipartiteGraph() self.assertRaises(KeyError, lambda: graph.removeEdge("supervisor1", "student1")) def test_case5(self): """Testing the removeEdge function when trying to remove an edge that doesn't exist""" self.assertRaises( ValueError, lambda: self.graph1.removeEdge("supervisor2", "student1")) def test_case6(self): """Testing the removeEdge function in a graph with existing nodes and edges""" graph = BipartiteGraph() graph.addEdge("supervisor1", "student1") graph.removeEdge("supervisor1", "student1") val1 = graph.getStudentDegree("student1") val2 = graph.getSupervisorDegree("supervisor1") self.assertEqual((val1, val2), (0, 0)) def test_case7(self): """Testing the merge function in two graphs""" graph3 = self.graph1.merge(self.graph2) expected = { 'supervisor1': ['student1', 'student2', 'student4'], 'supervisor2': ['student4', 'student1', 'student3'], 'supervisor3': ['student3', 'student2'] } result = True for sup in expected: for stu in expected[sup]: if not graph3.isEdge(sup, stu): result = False break self.assertTrue(result) def test_case8(self): """Testing the create random graph function""" graph = BipartiteGraph.createRandomGraph(self.students, self.supervisors) solution = Solution(graph) result = solution.isValid(self.students, self.supervisors) self.assertTrue(result) def test_case9(self): """Testing transfer function - Cannnot transfer student when a supervisor has less than 2 students""" self.graph1.transferStudent("student3", "supervisor3", "supervisor2", self.supervisors) val1 = self.graph1.getStudentDegree("student3") val2 = self.graph1.getSupervisors("student3") expected = (1, ['supervisor3']) self.assertEqual((val1, val2), expected) def test_case10(self): """Testing transfer function - cannot transfer student to a supervisor who reached their maximum quota""" self.graph1.transferStudent("student1", "supervisor1", "supervisor3", self.supervisors) val1 = self.graph1.getSupervisorDegree("supervisor1") val2 = self.graph1.getSupervisors("student1") val3 = self.graph1.getStudents("supervisor3") expected = (2, ['supervisor1'], ['student3']) self.assertEqual((val1, val2, val3), expected) def test_case11(self): """Testing transfer function - cannot transfer student if student doesn't exist in the graph""" self.graph1.transferStudent("student5", "supervisor1", "supervisor2", self.supervisors) val1 = self.graph1.getSupervisorDegree("supervisor1") val2 = self.graph1.getStudents("supervisor3") expected = (2, ['student3']) self.assertEqual((val1, val2), expected) def test_case12(self): """Testing transfer function - cannot transfer student if the edge doesn't exist in the graph""" self.graph1.transferStudent("student3", "supervisor1", "supervisor2", self.supervisors) val1 = self.graph1.getSupervisorDegree("supervisor1") val2 = self.graph1.getStudents("supervisor2") val3 = self.graph1.getStudents("supervisor1") expected = (2, ['student4'], ['student1', 'student2']) self.assertEqual((val1, val2, val3), expected) def test_case13(self): """Testing transfer function - cannot transfer student if edge already exists in the supervisor transferring to""" self.graph1.transferStudent("student4", "supervisor1", "supervisor2", self.supervisors) val1 = self.graph1.getSupervisorDegree("supervisor1") val2 = self.graph1.getStudents("supervisor2") val3 = self.graph1.getStudents("supervisor1") expected = (2, ['student4'], ['student1', 'student2']) self.assertEqual((val1, val2, val3), expected) def test_case14(self): """Testing transfer function - a valid transfer""" self.graph1.transferStudent("student2", "supervisor1", "supervisor2", self.supervisors) val1 = self.graph1.getStudents("supervisor1") val2 = self.graph1.getStudents("supervisor2") val3 = self.graph1.getSupervisors("student2") expected = (['student1'], ['student4', 'student2'], ['supervisor2']) self.assertEqual((val1, val2, val3), expected) def test_case15(self): """Testing supervisor Exists function - when a supervisor does not exist""" result = self.graph1.supervisorExists("supervisor4") self.assertFalse(result) def test_case16(self): """Testing supervisor Exists function - when a supervisor exists""" result = self.graph1.supervisorExists("supervisor1") self.assertTrue(result) def test_case17(self): """Testing student exists function - when a student does not exists""" result = self.graph1.studentExists("student5") self.assertFalse(result) def test_case18(self): """Testing student exists function - when a student exists""" result = self.graph1.studentExists("student1") self.assertTrue(result) def test_case19(self): """Testing isEdge function - when a supervisor does not exist in graph""" result = self.graph1.isEdge("supervisor5", "student1") self.assertFalse(result) def test_case20(self): """Testing isEdge function - when a student does not exist in graph""" result = self.graph1.isEdge("supervisor1", "student5") self.assertFalse(result) def test_case21(self): """Testing isEdge function - when a edge does not exist""" result = self.graph1.isEdge("supervisor2", "student1") self.assertFalse(result) def test_case22(self): """Testing isEdge function - when a edge exists""" result = self.graph1.isEdge("supervisor1", "student1") self.assertTrue(result) def test_case23(self): """Testing getStudents function""" result = self.graph1.getStudents("supervisor3") expected = ['student3'] self.assertEqual(result, expected) def test_case24(self): """Testing getSupervisors function""" result = self.graph1.getSupervisors("student1") expected = ['supervisor1'] self.assertEqual(result, expected) def test_case25(self): """Testing getSupervisorDegree function""" result = self.graph1.getSupervisorDegree("supervisor1") expected = 2 self.assertEqual(result, expected) def test_case26(self): """Testing getStudentDegree function""" result = self.graph1.getStudentDegree("student2") expected = 1 self.assertEqual(result, expected) def test_case27(self): """Testing getStructure function""" result = self.graph1.getStructure() expected = {'supervisor1': 2, 'supervisor2': 1, 'supervisor3': 1} self.assertEqual(result, expected) def test_case28(self): """Testing getEdges function""" result = self.graph1.getEdges() expected = { 'supervisor1': ['student1', 'student2'], 'supervisor2': ['student4'], 'supervisor3': ['student3'] } self.assertEqual(result, expected) def test_case29(self): """Testing getStuEdges function""" result = self.graph1.getStuEdges() expected = { 'student1': ['supervisor1'], 'student2': ['supervisor1'], 'student3': ['supervisor3'], 'student4': ['supervisor2'] } self.assertEqual(result, expected) def test_case30(self): """Testing getNumberofEdges function""" result = self.graph1.getNumberofEdges() expected = 4 self.assertEqual(result, expected) def test_case31(self): """Testing the swap function - when 2 non-existing pairs of edges are passed""" self.assertRaises( ValueError, lambda: self.graph1.swapStudents( "student1", "supervisor2", "student3", "supervisor1")) def test_case32(self): """Testing the swap function - when 2 non-existing students and supervisors are passed""" self.assertRaises( KeyError, lambda: self.graph1.swapStudents( "student5", "supervisor5", "student6", "supervisor6")) def test_case33(self): """Testing the swap function - when 2 existing edges are passed""" self.graph1.swapStudents("student1", "supervisor1", "student3", "supervisor3") result1 = self.graph1.getSupervisors("student3") result2 = self.graph1.getSupervisors("student1") expected1 = ['supervisor1'] expected2 = ['supervisor3'] self.assertEqual((result1, result2), (expected1, expected2)) def test_case34(self): """Testing the swap function - when swaping students of same supervisor""" self.graph1.swapStudents("student1", "supervisor1", "student2", "supervisor1") result1 = self.graph1.getSupervisors("student1") result2 = self.graph1.getSupervisors("student2") expected1 = ['supervisor1'] expected2 = ['supervisor1'] self.assertEqual((result1, result2), (expected1, expected2))
def kPoint(solution1, solution2, supervisors, students, k=5): """ An Implementation of K-Point crossover for this problem. """ graph1 = solution1.getGraph() graph2 = solution2.getGraph() stuEdges1 = graph1.getStuEdges() stuEdges2 = graph2.getStuEdges() supEdges1 = graph1.getEdges() #Randomly getting the structure from the two graphs num = random.randint(1, 2) if num == 1: structure = graph1.getStructure() else: structure = graph2.getStructure() #Setting up the vectors students = list(stuEdges1.keys()) sol1 = [] sol2 = [] for i in range(len(students)): sol1.append(stuEdges1[students[i]][0]) sol2.append(stuEdges2[students[i]][0]) #Dividing the both solutions into k-points sol1Points = [] sol2Points = [] points = sorted(random.sample(range(1, len(students)), k)) curr = 0 for i in range(k - 1): sol1Points.append(sol1[curr:points[i]]) sol2Points.append(sol2[curr:points[i]]) curr = points[i] sol1Points.append(sol1[curr:]) sol2Points.append(sol2[curr:]) #Perform the crossover result = [] if k == 0: n = random.randint(1, 2) if n == 1: result = sol1 else: result = sol2 else: for point in range(k): n = random.randint(1, 2) if n == 1: result.extend(sol1Points[point]) else: result.extend(sol2Points[point]) graph = BipartiteGraph() for i in range(len(students)): graph.addEdge(result[i], students[i]) fixSolution(graph, supervisors, students) #supEdges = graph.getEdges() #availableStudents = set() #reqSup = set(list(supEdges1.keys())).difference(set(list(supEdges.keys()))) #sol3 = Solution(graph) #canTransferFrom,canTransferTo = sol3.getTransferable(supervisors) #for sup in supEdges: # supDegree = len(supEdges[sup]) # supQuota = supervisors[sup].getQuota() # if supDegree > supQuota: # excess = supDegree - supQuota # for i in random.sample(supEdges[sup],excess): # availableStudents.add(i) # elif supDegree == 0: # reqSup.add(sup) #for sup in reqSup: # if len(availableStudents)==0: # fromSup = random.choice(list(canTransferFrom)) # stu = random.choice(graph.getStudents(fromSup)) # availableStudents.add(stu) # x = random.choice(list(availableStudents)) # availableStudents.remove(x) # oldSup = graph.getSupervisors(x)[0] # graph.transferStudent1(x,oldSup,sup,supervisors) #sol3 = Solution(graph) #canTransferFrom,canTransferTo = sol3.getTransferable(supervisors) #for stu in availableStudents: # con = False # oldSup = graph.getSupervisors(stu)[0] # if oldSup in canTransferTo: # canTransferTo.remove(oldSup) # con=True # toSup = random.choice(list(canTransferTo)) # graph.transferStudent1(stu,oldSup,toSup,supervisors) # if con: # canTransferTo.add(oldSup) # if not(graph.getSupervisorDegree(toSup) < supervisors[toSup].getQuota()): # canTransferTo.remove(toSup) return Solution(graph)
def crossover(solution1, solution2, supervisors=None, students=None, k=None): """ Function to perfrom crossover on two solutions. Parameters: solution1 (Solution) - parent solution 1 solution2 (Solution) - parent solution 2 Returns: A New Solution Object - an offspring solution from both the parent solutions. """ #Merging the two Graphs graph1 = solution1.getGraph() graph2 = solution2.getGraph() mergedGraph = graph1.merge(graph2) stuEdges = mergedGraph.getStuEdges() supEdges = mergedGraph.getEdges() #Randomly getting the structure from the two graphs stf1 = solution1.getStructuralFitness(supervisors) stf2 = solution2.getStructuralFitness(supervisors) if random.random() <= (stf1) / (stf1 + stf2): structure = graph1.getStructure() else: structure = graph2.getStructure() lockedEdges = set() lockedVertices = set() allStudents = set(list(stuEdges.keys())) result = BipartiteGraph() counts = { } #stores the degree of the supervisors in the new offspring graph for sup in supEdges: counts[sup] = 0 #Simplify first time here simplified = True prev_count = {} while prev_count != counts: prev_count = dict(counts) for sup in supEdges: supDegree = len(supEdges[sup]) reqDegree = structure[sup] for stu in supEdges[sup]: if len(stuEdges[stu]) == 1 and not stu in lockedVertices: mergedGraph.removeExcept(sup, stu) result.addEdge(sup, stu) lockedVertices.add(stu) counts[sup] += 1 if counts[sup] == reqDegree: toKeep = result.getStudents(sup) toRemove = mergedGraph.getRemainingStu(sup, toKeep) for i in toRemove: mergedGraph.removeEdge(sup, i) prev = set() toContinue = False while len(lockedVertices) != len(stuEdges): for sup in supEdges: #If the supervisor degree is not equal to degree we want if counts[sup] != structure[sup]: supDegree = mergedGraph.getSupervisorDegree(sup) reqSupDegree = structure[sup] students = mergedGraph.getStudents(sup) #Pick a random student that is not locked from the supervior's list of students curr = random.choice(students) if (curr not in lockedVertices): #If that edge can be locked, then lock it. if mergedGraph.canLock(sup, curr, structure, counts, lockedVertices): #Remove other supervisors in student's list of supervisors mergedGraph.removeExcept(sup, curr) #Add it to the new graph and also locked vertices result.addEdge(sup, curr) lockedVertices.add(curr) #Increment the degree of the supervisor counts[sup] += 1 #If the degee is the degree we want if counts[sup] == reqSupDegree: toKeep = result.getStudents( sup) #Get the students we want to keep toRemove = mergedGraph.getRemainingStu( sup, toKeep) #Get students we dont want to keep #Remove those students (edges) for stu in toRemove: mergedGraph.removeEdge(sup, stu) #If we can lock any further, then we break the loop if len(prev) != len(lockedVertices): prev = set(list(lockedVertices)) else: toContinue = True break #Allocate remaining students to supervisors that don't meet the required degree if toContinue: availableStudents = allStudents.difference(lockedVertices) for sup in supEdges: reqDegree = structure[sup] supDegree = counts[sup] supNeeds = reqDegree - supDegree if supDegree != reqDegree: toAdd = random.sample(availableStudents, supNeeds) for stu in toAdd: result.addEdge(sup, stu) lockedVertices.add(stu) counts[sup] += supNeeds availableStudents.remove(stu) return Solution(result)