From 88c14dba014d2750ad68f6edb021e6eacaad8185 Mon Sep 17 00:00:00 2001 From: oriariel Date: Sun, 22 Jan 2023 18:08:10 +0200 Subject: [PATCH 1/5] added course allocation --- fairpy/course_allocation/Course.py | 15 ++ fairpy/course_allocation/Student.py | 17 ++ fairpy/course_allocation/TabuList.py | 16 ++ fairpy/course_allocation/algorithm1.py | 221 ++++++++++++++++++++ fairpy/course_allocation/algorithm1_test.py | 69 ++++++ fairpy/course_allocation/algorithm2.py | 143 +++++++++++++ fairpy/course_allocation/algorithm2_test.py | 58 +++++ fairpy/course_allocation/algorithm3.py | 124 +++++++++++ fairpy/course_allocation/algorithm3_test.py | 52 +++++ fairpy/course_allocation/demo.py | 89 ++++++++ 10 files changed, 804 insertions(+) create mode 100644 fairpy/course_allocation/Course.py create mode 100644 fairpy/course_allocation/Student.py create mode 100644 fairpy/course_allocation/TabuList.py create mode 100644 fairpy/course_allocation/algorithm1.py create mode 100644 fairpy/course_allocation/algorithm1_test.py create mode 100644 fairpy/course_allocation/algorithm2.py create mode 100644 fairpy/course_allocation/algorithm2_test.py create mode 100644 fairpy/course_allocation/algorithm3.py create mode 100644 fairpy/course_allocation/algorithm3_test.py create mode 100644 fairpy/course_allocation/demo.py diff --git a/fairpy/course_allocation/Course.py b/fairpy/course_allocation/Course.py new file mode 100644 index 00000000..cdc17921 --- /dev/null +++ b/fairpy/course_allocation/Course.py @@ -0,0 +1,15 @@ +from copy import deepcopy + +class Course: + def __init__(self, name:str, price:float, max_capacity:int, capacity:int = 0) -> None: + self.name = name + self.price = price + self.capacity = capacity + self.max_capacity = max_capacity + + def comperator(a, b): + return (a.capacity - a.max_capacity) - (b.capacity - b.max_capacity) + + + def __str__(self) -> str: + return f"course name: {self.name} capacity {self.capacity}/{self.max_capacity} and priced {self.price}" \ No newline at end of file diff --git a/fairpy/course_allocation/Student.py b/fairpy/course_allocation/Student.py new file mode 100644 index 00000000..35c2dcb2 --- /dev/null +++ b/fairpy/course_allocation/Student.py @@ -0,0 +1,17 @@ +from copy import deepcopy +from Course import Course +class Student: + def __init__(self,name, budget, preferences, courses = [], year=1): + self.name = name + self.budget = budget + self.courses = courses + self.preferences = preferences + self.year = year + + def __str__(self) -> str: + return self.name + " year: " + str(self.year) + "with budget of (" + str(self.budget) + ") \n" + str(self.courses) + "\n " + str(self.preferences) + + def comperator(a, b): + if(a.year - b.year == 0): + return (a.budget - b.budget) + return a.year - b.year \ No newline at end of file diff --git a/fairpy/course_allocation/TabuList.py b/fairpy/course_allocation/TabuList.py new file mode 100644 index 00000000..3a4feab1 --- /dev/null +++ b/fairpy/course_allocation/TabuList.py @@ -0,0 +1,16 @@ +class TabuList: + def __init__(self, size): + self.size = size + self.items = [] + + def add(self, item): + if item not in self.items: + self.items.append(item) + while len(self.items) > self.size: + self.items.pop(0) + + def __iter__(self): + return iter(self.items) + + + \ No newline at end of file diff --git a/fairpy/course_allocation/algorithm1.py b/fairpy/course_allocation/algorithm1.py new file mode 100644 index 00000000..5121e68c --- /dev/null +++ b/fairpy/course_allocation/algorithm1.py @@ -0,0 +1,221 @@ +''' +Heuristic search algorithm through price space, originally developed in Othman et al. 2010 +designed to give each student a fixed budget at first and try find the must corresponding +price vector to approximate competitive equilibrium with lowest clearing error. +programmers : Aviv Danino & Ori Ariel +''' + + +from Course import Course +from Student import Student +from functools import cmp_to_key +from TabuList import TabuList +import numpy as np +import time +import random +import copy +import doctest +import logging + + +logger = logging.getLogger(__name__) +console = logging.StreamHandler() +logfile = logging.FileHandler("my_logger1.log", mode="w") +logger.handlers = [console,logfile] +logfile.setFormatter(logging.Formatter('%(asctime)s: %(levelname)s: %(name)s: Line %(lineno)d: %(message)s')) + +logger.setLevel(logging.DEBUG) +console.setLevel(logging.INFO) + +def reset_update_prices(price_vector, courses): + ''' + reset prices of courses and capacity + >>> a = Course(name='a', price=0, max_capacity=5) + >>> b = Course(name='b', price=0, max_capacity=3) + >>> c = Course(name='c', price=0, max_capacity=5) + >>> courses = [a, b, c] + + >>> s1 = Student(name='s1', budget=15, preferences=([c, b])) + >>> s2 = Student(name='s2', budget=15, preferences=([b, c, a])) + >>> s3 = Student(name='s3', budget=15, preferences=([b, a])) + >>> s4 = Student(name='s4', budget=15, preferences=([a, c])) + >>> s5 = Student(name='s5', budget=15, preferences=([a, b, c])) + >>> students = [s1, s2, s3, s4, s5] + >>> max_budget = 15 + >>> reset_update_prices([7,8,9], courses) + >>> [course.capacity for course in courses] + [0, 0, 0] + >>> [course.price for course in courses] + [7, 8, 9] + ''' + i = 0 + for course in courses: + course.capacity = 0 + course.price = price_vector[i] + i += 1 + +def reset_students(students, max_budget: float): + ''' + reset budget and courses for students + >>> a = Course(name='a', price=0, max_capacity=5) + >>> b = Course(name='b', price=0, max_capacity=3) + >>> c = Course(name='c', price=0, max_capacity=5) + >>> courses = [a, b, c] + + >>> s1 = Student(name='s1', budget=0, preferences=([c, b])) + >>> s2 = Student(name='s2', budget=0, preferences=([b, c, a])) + >>> s3 = Student(name='s3', budget=0, preferences=([b, a])) + >>> s4 = Student(name='s4', budget=0, preferences=([a, c])) + >>> s5 = Student(name='s5', budget=0, preferences=([a, b, c])) + >>> students = [s1, s2, s3, s4, s5] + >>> max_budget = 15 + >>> reset_students(students, max_budget) + >>> [student.budget for student in students] + [15, 15, 15, 15, 15] + >>> [student.courses for student in students] + [[], [], [], [], []] + ''' + for student in students: + student.budget = max_budget + student.courses = [] + +def map_price_demand(price_vector, max_budget: float, students, + courses): + ''' + ****adding more documention about here + mapping price vector to demands of students + >>> a = Course(name='a', price=0, capacity = 0, max_capacity=5) + >>> b = Course(name='b', price=0, capacity = 0,max_capacity=3) + >>> c = Course(name='c', price=0, capacity = 0,max_capacity=5) + >>> courses = [a, b, c] + + >>> s1 = Student(name='s1', budget=0, preferences=([c, b])) + >>> s2 = Student(name='s2', budget=0, preferences=([b, c, a])) + >>> s3 = Student(name='s3', budget=0, preferences=([b, a])) + >>> s4 = Student(name='s4', budget=0, preferences=([a, c])) + >>> s5 = Student(name='s5', budget=0, preferences=([a, b, c])) + >>> students = [s1, s2, s3, s4, s5] + >>> max_budget = 15 + >>> price_vector = [7,8,7] + >>> map_price_demand(price_vector, max_budget, students, courses) + >>> [str(course) for course in courses] + ['course name: a capacity 3/5 and priced 7', 'course name: b capacity 4/3 and priced 8', 'course name: c capacity 3/5 and priced 7'] + ''' + #different functions to each + #testing working for changes in function -- DELETE + reset_update_prices(price_vector, courses) + reset_students(students, max_budget) + def get_course(s:Student): + for preference in s.preferences: + if (s.budget >= preference.price and + preference not in s.courses): + s.courses.append(preference) + s.budget = s.budget - preference.price + preference.capacity += 1 + for student in students: + if (len(student.preferences) > len(student.courses)): + get_course(student) + +#function returning alpha error +def alpha_error(price_vector, max_budget, students, courses): + map_price_demand(price_vector, max_budget, students, courses) + s = 0 + for course in courses: + s = s + (course.max_capacity - course.capacity)**2 + return s + +# +def get_demand_vector(courses): + lst = [] + for course in courses: + lst.append(f"{course.capacity}/{course.max_capacity}") + return lst + + +def algorithm1(students, courses, max_budget:float, time_to:float, seed:int = 3, counter = 0): + ''' + Heuristic search algorithm through price space, originally developed in Othman et al. 2010 + designed to give each student a fixed budget at first and try find the must corresponding + price vector to approximate competitive equilibrium with lowest clearing error. + programmers : Aviv Danino & Ori Ariel + + counter : parameter designed to help for testing in different machines + + >>> a = Course(name='a', price=0, capacity=0, max_capacity=5) + >>> b = Course(name='b', price=0, capacity=0, max_capacity=4) + >>> c = Course(name='c', price=0, capacity=0, max_capacity=5) + >>> courses = [a, b, c] + >>> s1 = Student(name='s1', budget=18, year=1, courses=[], preferences=([c, b])) + >>> s2 = Student(name='s2', budget=18, year=1, courses=[], preferences=([b, c, a])) + >>> s3 = Student(name='s3', budget=18, year=1, courses=[], preferences=([b, a])) + >>> s4 = Student(name='s4', budget=18, year=1, courses=[], preferences=([a, b, c])) + >>> s5 = Student(name='s5', budget=18, year=1, courses=[], preferences=([a, b, c])) + >>> s6 = Student(name='s6', budget=18, year=1, courses=[], preferences=([a, c, b])) + >>> students = [s1, s2, s3, s4, s5, s6] + >>> max_budget = 18 + >>> algorithm1(students, courses, max_budget, time_to= 2.0, seed = 3) + [4.86, 2.34, 6.66] + ''' + logger_counter = 0 + if time_to < 0.01: + logger.warning("to little time can crash the program") + pStar = [] + start_time = time.time() + best_error = float("inf") + #for better testing + random_state = np.random.RandomState(seed=3) + while(time.time() - start_time < time_to): + price_vector = [((random_state.randint(low=1,high=99)/100)*max_budget) for i in range(len(courses))] + search_error = alpha_error(price_vector, max_budget, students, courses) + tabu_list = TabuList(5) + c = 0 + while c < tabu_list.size: + queue = [] + for i in range(0, len(price_vector)): + temp = copy.deepcopy(price_vector) + temp[i] = (random_state.randint(low=1,high=99)/100)*max_budget + queue.append(temp) + queue = sorted(queue, key=lambda x: alpha_error(x, max_budget, students, courses)) + found_step = False + while(queue and not found_step):#3 + temp = queue.pop() + #need function for demand check in tabu list + map_price_demand(temp, max_budget, students, courses) + if get_demand_vector(courses) not in tabu_list: + logger.debug("%s", str(temp)) + found_step = True + #end of while 3 + if(not queue) : c = tabu_list.size + else: + price_vector = temp + #add the demands and not the price vector + current_error = alpha_error(price_vector, max_budget, students, courses) + tabu_list.add(get_demand_vector(courses)) + logger.debug((get_demand_vector(courses))) + if(current_error < search_error): + search_error = current_error + logger.debug("search error is %d", search_error) + c = 0 + else: + c = c + 1 + #end of if + if(current_error < best_error): + logger_counter += 1 + best_error = current_error + pStar = price_vector + logger.info("best error is %d ", best_error) + logger.debug("and its price vector is %s", pStar) + if(logger_counter == counter): + time_to = 0 + c = tabu_list.size + + #end of if + #end of while 2 + #end of while 1 + + logger.debug(logger_counter) + return pStar +if __name__=="__main__": + import pytest + #run algorithm and test of the algorithm + pytest.main(args=["fairpy/course_allocation/algorithm1.py", "fairpy/course_allocation/algorithm1_test.py"]) diff --git a/fairpy/course_allocation/algorithm1_test.py b/fairpy/course_allocation/algorithm1_test.py new file mode 100644 index 00000000..5b06f995 --- /dev/null +++ b/fairpy/course_allocation/algorithm1_test.py @@ -0,0 +1,69 @@ +from algorithm1 import algorithm1,Course,Student,random,TabuList,copy,map_price_demand,reset_update_prices,cmp_to_key,reset_students,time +def test1(): + a = Course(name='a', price=0, max_capacity=5) + b = Course(name='b', price=0, max_capacity=3) + c = Course(name='c', price=0, max_capacity=5) + courses = [a, b, c] + + s1 = Student(name='s1', budget=15, preferences=([c, b])) + s2 = Student(name='s2', budget=15, preferences=([b, c, a])) + s3 = Student(name='s3', budget=15, preferences=([b, a])) + s4 = Student(name='s4', budget=15, preferences=([a, c])) + s5 = Student(name='s5', budget=15, preferences=([a, b, c])) + students = [s1, s2, s3, s4, s5] + max_budget = 15 + assert(algorithm1(students, courses, max_budget, time_to= 1, seed = 3, counter=4) == [4.050000000000001, 1.9500000000000002, 5.55]) + +def test2(): + a = Course(name='a', price=0, max_capacity=3) + b = Course(name='b', price=0, max_capacity=3) + c = Course(name='c', price=0, max_capacity=3) + courses = [a, b, c] + s1 = Student(name='s1', budget=10, courses=[], preferences=([a])) + s2 = Student(name='s2', budget=10, courses=[], preferences=([b,a])) + s3 = Student(name='s3', budget=10, courses=[], preferences=([c])) + s4 = Student(name='s4', budget=10, courses=[], preferences=([c,b])) + s5 = Student(name='s5', budget=10, courses=[], preferences=([c, a, b])) + students = [s1, s2, s3, s4, s5] + + max_budget = 10 + assert(algorithm1(students, courses, max_budget, time_to= 1, seed = 3, counter=2) == [2.7, 1.3, 3.7]) + +def test3(): + a = Course(name='a', price=0, capacity=0, max_capacity=5) + b = Course(name='b', price=0, capacity=0, max_capacity=4) + c = Course(name='c', price=0, capacity=0, max_capacity=5) + d = Course(name='d', price=0, capacity=0, max_capacity=3) + e = Course(name='e', price=0, capacity=0, max_capacity=7) + f = Course(name='f', price=0, capacity=0, max_capacity=2) + courses = [a, b, c, d, e, f] + s1 = Student(name='s1', budget=18, year=1, courses=[], preferences=([c, b, e, f])) + s2 = Student(name='s2', budget=18, year=1, courses=[], preferences=([b, c, a, e])) + s3 = Student(name='s3', budget=18, year=1, courses=[], preferences=([b, a, d])) + s4 = Student(name='s4', budget=18, year=1, courses=[], preferences=([a, b, c])) + s5 = Student(name='s5', budget=18, year=1, courses=[], preferences=([d, a,e, b, c])) + s6 = Student(name='s6', budget=18, year=1, courses=[], preferences=([a, e,c,f, b])) + s7 = Student(name='s7', budget=18, year=1, courses=[], preferences=([c,d ,b])) + s8 = Student(name='s8', budget=18, year=1, courses=[], preferences=([b, c, a])) + s9 = Student(name='s9', budget=18, year=1, courses=[], preferences=([b,f,e, a])) + s10 = Student(name='s10', budget=18, year=1, courses=[], preferences=([d ,f])) + s11 = Student(name='s11', budget=18, year=1, courses=[], preferences=([d,e,f])) + s12 = Student(name='s12', budget=18, year=1, courses=[], preferences=([d,f,e])) + students = [s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12] + max_budget = 18 + assert(algorithm1(students, courses, max_budget, time_to= 1, seed = 3, counter = 3) == [3.96, 12.6, 9.9, 4.68, 1.08, 4.140000000000001])#7 + +if __name__=="__main__": + import pytest + pytest.main(args=["fairpy/course_allocation"]) + +''' +disclaimer for testing +the tests are not conclusive since computing power may vary +and they using time which work differently in different machines. +that worked on laptop with this specs: +cpu = i5 6200u +os = ubuntu +graphics card = None +and youtube in the background... +''' \ No newline at end of file diff --git a/fairpy/course_allocation/algorithm2.py b/fairpy/course_allocation/algorithm2.py new file mode 100644 index 00000000..c026033c --- /dev/null +++ b/fairpy/course_allocation/algorithm2.py @@ -0,0 +1,143 @@ +''' +The algorithm takes input a price vector generated from Algorithm 1, +a scalar price that is greater than any budget, +a value for the budget differences, +a function "csp_mapping" that maps the demand of a course beyond its maximum capacity. +returns: an altered version of p* (price vector) that does not have any oversubscription. +programmers : Aviv Danino & Ori Ariel +''' + +import copy +import math +from Course import Course +from Student import Student +from functools import cmp_to_key +import doctest +import logging + +logger = logging.getLogger(__name__) +console = logging.StreamHandler() +logfile = logging.FileHandler("my_logger2.log", mode="w") +logger.handlers = [console,logfile] +logfile.setFormatter(logging.Formatter('%(asctime)s: %(levelname)s: %(name)s: Line %(lineno)d: %(message)s')) + +logger.setLevel(logging.DEBUG) +console.setLevel(logging.WARNING) + +def csp_mapping(students,courses): + ''' + >>> a = Course(name='a', price=2.4, capacity=0, max_capacity=5) + >>> b = Course(name='b', price=5, capacity=0, max_capacity=4) + >>> c = Course(name='c', price=10.4, capacity=0, max_capacity=5) + >>> courses = [a, b, c] + >>> s1 = Student(name='s1', budget=18, year=1, courses=[], preferences=([c, b])) + >>> s2 = Student(name='s2', budget=18, year=1, courses=[], preferences=([b, c, a])) + >>> s3 = Student(name='s3', budget=18, year=1, courses=[], preferences=([b, a])) + >>> s4 = Student(name='s4', budget=18, year=1, courses=[], preferences=([a, c])) + >>> s5 = Student(name='s5', budget=18, year=1, courses=[], preferences=([a, b, c])) + >>> s6 = Student(name='s6', budget=18, year=1, courses=[], preferences=([a, c, b])) + >>> students = [s1, s2, s3, s4, s5, s6] + >>> csp_mapping(students, courses) + >>> [str(course) for course in courses] + ['course name: a capacity 5/5 and priced 2.4', 'course name: b capacity 4/4 and priced 5', 'course name: c capacity 5/5 and priced 10.4'] + ''' + def get_course(s) -> bool: + for preference in s.preferences: + if (s.budget >= preference.price and + preference not in s.courses + and preference.capacity < preference.max_capacity): + s.courses.append(preference) + s.budget = s.budget - preference.price + preference.capacity += 1 + return True + return False + for course in courses: + course.capacity = 0 + while True: + flag = False + for student in students: + if (len(student.preferences) > len(student.courses)): + if (get_course(student)): + flag = True + if(not flag): + break +def copy_students(students): + list_copy = [] + for s in students: + a = copy.deepcopy(s) + a.preferences = [] + for pre in s.preferences: + a.preferences.append(pre) + list_copy.append(a) + return list_copy + +def get_demand_vector(courses): + lst = [] + for course in courses: + lst.append(f"{course.capacity}/{course.max_capacity}") + return lst + +def algorithm2(price_vector, maximum:int, eps:float, csp_mapping:callable, students, courses): + ''' + Iterative oversubscription elimination algorithm, + reducing by half the excess demand of the most oversubscribed course with each pass, + :param price_vector: given price vector + :param maximum: suprimum for the price + :param eps: error tolerance + :param csp_mapping: function to mapping students to courses + :param students: students list + :param courses: list of courses + :return: price vector to prevent oversubscription + >>> a = Course(name='a', price=6, capacity=0, max_capacity=3) + >>> b = Course(name='b', price=4, capacity=0, max_capacity=3) + >>> c = Course(name='c', price=6, capacity=0, max_capacity=3) + >>> courses = [a, b, c] + >>> s1 = Student(name='s1', budget=10, courses=[], preferences=([a])) + >>> s2 = Student(name='s2', budget=10, courses=[], preferences=([b,a])) + >>> s3 = Student(name='s3', budget=10, courses=[], preferences=([c])) + >>> s4 = Student(name='s4', budget=10, courses=[], preferences=([c,b])) + >>> s5 = Student(name='s5', budget=10, courses=[], preferences=([c, a, b])) + >>> students = [s1, s2, s3, s4, s5] + >>> eps = 1 + >>> maximum = 10 + >>> price_vector = [6,4,6] + >>> algorithm2(price_vector, maximum, eps, csp_mapping, students, courses) + [6, 4, 6] + ''' + ## this section is for finding j_hat in line 1 + if(max([student.budget for student in students]) > maximum): + raise Exception("maximum must be greater than max budget of students or more") + stud = copy_students(students) + flag = False + logger.debug("%g is the greatest budget", max([student.budget for student in students])) + csp_mapping(stud, courses) + J_hat = max(courses, key=cmp_to_key(Course.comperator)) + + ##till here line 1 in algorithm 2 + while(J_hat.capacity-J_hat.max_capacity > 0): #2 + d_star = math.floor((J_hat.capacity-J_hat.max_capacity)/2) #3 + p_l = J_hat.price #4 + p_h = maximum #5 + logger.debug("low : %g", p_l) + logger.debug("high : %g", p_h) + while(True): #6 + J_hat.price = (p_l + p_h)/2 #7 + ##same here as above, for line 8 if section + stud = copy_students(students) + csp_mapping(stud,courses) # mapping here after price changes + if((J_hat.capacity-J_hat.max_capacity) >= d_star): #line 8 + p_l = J_hat.price #9 + else: #10 + p_h = J_hat.price #11 + if(p_h - p_l <= eps): #13 + J_hat.price = p_h #14 + break #part of 13 + logger.debug("new price %g for course %s", J_hat.price, J_hat.name) + J_hat = max(courses, key=cmp_to_key(Course.comperator)) #15 + logger.info(get_demand_vector(courses)) + return [c.price for c in courses] #return at the end + +if __name__=="__main__": + import pytest + # #run algorithm and test of the algorithm + pytest.main(args=["fairpy/course_allocation/algorithm2.py", "fairpy/course_allocation/algorithm2_test.py"]) \ No newline at end of file diff --git a/fairpy/course_allocation/algorithm2_test.py b/fairpy/course_allocation/algorithm2_test.py new file mode 100644 index 00000000..f8d9a5ec --- /dev/null +++ b/fairpy/course_allocation/algorithm2_test.py @@ -0,0 +1,58 @@ +from algorithm2 import algorithm2,Course,Student,csp_mapping,copy,math,cmp_to_key + +def test_1(): + a = Course(name='a', price=9, max_capacity=5) + b = Course(name='b', price=2, max_capacity=3) + c = Course(name='c', price=4.5, max_capacity=5) + s1 = Student(name='s1', budget=15, preferences=([c, b])) + s2 = Student(name='s2', budget=15, preferences=([b, c, a])) + s3 = Student(name='s3', budget=15, preferences=([b, a])) + s4 = Student(name='s4', budget=15, preferences=([a, c])) + s5 = Student(name='s5', budget=15, preferences=([a, b, c])) + courses = [a, b, c] + students = [s1, s2, s3, s4, s5] + + eps = 1 + maximum = 15 + price_vector = [9,2,4.5] + assert algorithm2(price_vector, maximum, eps, csp_mapping, students, courses) == [9, 2, 4.5] + +def test_2(): + a = Course(name='a', price=2.4, capacity=0, max_capacity=5) + b = Course(name='b', price=5, capacity=0, max_capacity=4) + c = Course(name='c', price=10.4, capacity=0, max_capacity=5) + courses = [a, b, c] + + + s1 = Student(name='s1', budget=18, year=1, courses=[], preferences=([c, b])) + s2 = Student(name='s2', budget=18, year=1, courses=[], preferences=([b, c, a])) + s3 = Student(name='s3', budget=18, year=1, courses=[], preferences=([b, a])) + s4 = Student(name='s4', budget=18, year=1, courses=[], preferences=([a, c])) + s5 = Student(name='s5', budget=18, year=1, courses=[], preferences=([a, b, c])) + s6 = Student(name='s6', budget=18, year=1, courses=[], preferences=([a, c, b])) + students = [s1, s2, s3, s4, s5, s6] + eps = 1 + maximum = 18 + price_vector = [2.4,5,10.4] + assert algorithm2(price_vector, maximum, eps, csp_mapping, students, courses) == [2.4, 5, 10.4] + + +def test_3(): + a = Course(name='a', price=3, max_capacity=5) + b = Course(name='b', price=4, max_capacity=3) + c = Course(name='c', price=3, max_capacity=5) + courses = [a, b, c] + s1 = Student(name='s1', budget=10, preferences=([c, b])) + s2 = Student(name='s2', budget=10, preferences=([b, c, a])) + s3 = Student(name='s3', budget=10, preferences=([b, a])) + s4 = Student(name='s4', budget=10, preferences=([a, b, c])) + s5 = Student(name='s5', budget=10, preferences=([a, b, c])) + students = [s1, s2, s3, s4, s5] + eps = 0.1 + maximum = 10 + price_vector = [3,4,3] + assert(algorithm2(price_vector, maximum, eps, csp_mapping, students, courses)==[3,4,3]) + +if __name__=="__main__": + import pytest + pytest.main(args=["fairpy/course_allocation"]) \ No newline at end of file diff --git a/fairpy/course_allocation/algorithm3.py b/fairpy/course_allocation/algorithm3.py new file mode 100644 index 00000000..ed84157b --- /dev/null +++ b/fairpy/course_allocation/algorithm3.py @@ -0,0 +1,124 @@ +''' +The algorithm takes input of student-course allocations +and restricted demand functions based on students, class, year and budget surplus. +It then modifies the allocations based on the +input demand functions and the ordering of the students, +returns an altered allocations of students to courses. +''' + + +from Course import Course +from Student import Student +from functools import cmp_to_key +import doctest +import logging +logger = logging.getLogger(__name__) +console = logging.StreamHandler() # writes to stderr (= cerr) +logfile = logging.FileHandler("my_logger3.log", mode="w") +logger.handlers = [console,logfile] +logfile.setFormatter(logging.Formatter('%(asctime)s: %(levelname)s: %(name)s: Line %(lineno)d: %(message)s')) + +logger.setLevel(logging.DEBUG) +console.setLevel(logging.INFO) + +def mapping_csp(courses, students, helper:dict, students_matrix): + ''' + >>> a = Course(name='a', price=12, capacity=1 ,max_capacity=5) + >>> b = Course(name='b', price=2, capacity=3 ,max_capacity=3) + >>> c = Course(name='c', price=4.5, capacity=3 ,max_capacity=5) + >>> courses = [a, b, c] + + >>> s1 = Student(name='s1', budget=10.5, courses=[b,c],preferences=([c, b])) + >>> s2 = Student(name='s2', budget=10.5, courses=[b,c] ,year=2 ,preferences=([b, c, a])) + >>> s3 = Student(name='s3', budget=15, courses=[b], year=3, preferences=([b, a])) + >>> s4 = Student(name='s4', budget=0.5, courses=[a,c], year=3, preferences=([a, c])) + >>> s5 = Student(name='s5', budget=4, courses=[a], preferences=([a, b, c])) + >>> students = [s1, s2, s3, s4, s5] + + >>> students_matrix = [[False,True,True],[False,True,True],[False,True,False],[True,False,True],[True,False,False]] + >>> helper = {} + >>> count = 0 + >>> for course in courses: + ... helper[course.name] = count + ... count += 1 + >>> mapping_csp( courses ,students, helper ,students_matrix) + >>> [str(course) for course in courses] + ['course name: a capacity 2/5 and priced 12', 'course name: b capacity 3/3 and priced 2', 'course name: c capacity 3/5 and priced 4.5'] + ''' + def get_course(s: Student, stud_num:int) -> bool: + for preference in s.preferences: + if (s.budget >= preference.price and + preference not in s.courses and + students_matrix[stud_num][helper.get(preference.name)] == False and + preference.capacity < preference.max_capacity): + s.courses.append(preference) + s.budget = s.budget - preference.price + preference.capacity += 1 + students_matrix[stud_num][helper.get(preference.name)] = True + logger.debug("change in (%g, %g) in matrix from 0 to 1", stud_num, helper.get(preference.name)) + return True + return False + while True: + flag = False + i = 0 + for student in students: + if (len(student.preferences) > len(student.courses)): + if (get_course(student,i)): + flag = True + i += 1 + if (not flag): + break + +def print_matrix(students, courses, students_matrix): + s = "" + for i in range(len(students)): + for j in range(len(courses)): + s+=str(students_matrix[i][j]) + s+= ", " + s+="\n" + return s + +def algorithm3(courses, students, students_matrix,csp_students:callable =mapping_csp): + ''' + Automated aftermarket allocations with increased budget and restricted allocations + >>> a = Course(name='a', price= 12, capacity =3 , max_capacity=5) + >>> b = Course(name='b', price = 2, capacity =4 , max_capacity=4) + >>> c = Course(name='c', price=4.5, capacity =3 , max_capacity=5) + >>> courses = [a, b, c] + >>> s1 = Student(name='s1', budget=10.5, year=2,courses=[c,b], preferences=([c, b])) + >>> s2 = Student(name='s2', budget=10.5, year=2,courses=[c,b], preferences=([b, c, a])) + >>> s3 = Student(name='s3', budget=3, year=2,courses=[b,a], preferences=([b, a, c])) + >>> s4 = Student(name='s4', budget=3, year=2,courses=[b,a], preferences=([a, b, c])) + >>> s5 = Student(name='s5', budget=0.5, year=2,courses=[a,c], preferences=([a, b, c])) + >>> students = [s1, s2, s3, s4, s5] + >>> students_matrix = [[False, True, True], [False, True, True], [True, True, False], [True, True, False], [True, False, True]] + >>> algorithm3(courses, students ,students_matrix,mapping_csp) + [[False, True, True], [False, True, True], [True, True, False], [True, True, False], [True, False, True]] + ''' + logger.debug(print_matrix(students, courses, students_matrix)) + #create dict for easy traversal + helper = {} + count = 0 + for course in courses: + helper[course.name] = count + count += 1 + ## not part of algorithm + csp_students(courses, students, helper, students_matrix) + done = False + while(not done): #1 + done = True + for student in sorted(students, key=cmp_to_key(Student.comperator),reverse=True): + before_budget = student.budget + before_inc = student.courses + student.budget *= 1.1 + csp_students(courses, students, helper, students_matrix) + if(not(before_inc == student.courses)): + done = False + break #break of for loop and not while loop + logger.debug(logger.debug(print_matrix(students, courses, students_matrix))) + return students_matrix + +if __name__=="__main__": + import pytest + #run algorithm and test of the algorithm + pytest.main(args=["fairpy/course_allocation/algorithm3.py", "fairpy/course_allocation/algorithm3_test.py"]) \ No newline at end of file diff --git a/fairpy/course_allocation/algorithm3_test.py b/fairpy/course_allocation/algorithm3_test.py new file mode 100644 index 00000000..68ad7778 --- /dev/null +++ b/fairpy/course_allocation/algorithm3_test.py @@ -0,0 +1,52 @@ +from algorithm3 import algorithm3,Course,Student,mapping_csp + +def test1(): + a = Course(name='a', price=12, capacity=1 ,max_capacity=5) + b = Course(name='b', price=2, capacity=3 ,max_capacity=3) + c = Course(name='c', price=4.5, capacity=3 ,max_capacity=5) + courses = [a, b, c] + + s1 = Student(name='s1', budget=10.5, courses=[b,c],preferences=([c, b])) + s2 = Student(name='s2', budget=10.5, courses=[b,c] ,year=2 ,preferences=([b, c, a])) + s3 = Student(name='s3', budget=15, courses=[b], year=3, preferences=([b, a])) + s4 = Student(name='s4', budget=0.5, courses=[a,c], year=3, preferences=([a, c])) + s5 = Student(name='s5', budget=4, courses=[a], preferences=([a, b, c])) + students = [s1, s2, s3, s4, s5] + + students_matrix = [[False,True,True],[False,True,True],[False,True,False],[True,False,True],[True,False,False]] + assert algorithm3(courses, students, students_matrix,mapping_csp) == [[False, True, True], [False, True, True], [True, True, False], [True, False, True], [True, False, False]] + +def test2(): + a = Course(name='a', price=3.4, capacity=4, max_capacity=5) + b = Course(name='b', price=5, capacity=4, max_capacity=4) + c = Course(name='c', price=10.4, capacity=4, max_capacity=5) + courses = [a,b,c] + s1 = Student(name='s1', budget=2.6, year=1, courses=[c,b], preferences=([c, b])) + s2 = Student(name='s2', budget=2.6, year=2, courses=[c,b], preferences=([b, c, a])) + s3 = Student(name='s3', budget=9.6, year=3, courses=[a,b], preferences=([b, a])) + s4 = Student(name='s4', budget=4.2, year=3, courses=[a,c], preferences=([a, c])) + s5 = Student(name='s5', budget=9.6, year=1, courses=[a,b], preferences=([a, b, c])) + s6 = Student(name='s6', budget=4.2, year=3, courses=[a,c], preferences=([a, c, b])) + students = [s1, s2, s3, s4, s5, s6] + students_matrix = [[False, True, True], [False, True, True], [True, True, False], [True, False, True], [True, True, False], [True, False, True]] + assert algorithm3(courses, students, students_matrix, mapping_csp) == [[False,True,True],[False,True,True],[True,True,False],[True,False,True],[True,True,True],[True,False,True]] + + + +def test3(): + a = Course(name='a', price=6, capacity=3, max_capacity=3) + b = Course(name='b', price=4, capacity=3, max_capacity=3) + c = Course(name='c', price=6, capacity=3, max_capacity=3) + courses = [a, b, c] + s1 = Student(name='s1', budget=4, year= 1,courses=[a], preferences=([a])) + s2 = Student(name='s2', budget=0, year= 2,courses=[a,b], preferences=([b,a])) + s3 = Student(name='s3', budget=4, year= 3,courses=[c], preferences=([c])) + s4 = Student(name='s4', budget=0, year= 3,courses=[b,c], preferences=([c,b])) + s5 = Student(name='s5', budget=0, year= 2,courses=[b,c], preferences=([c, a, b])) + students = [s1, s2, s3, s4, s5] + students_matrix = [[True,False,False],[True,True,False],[False,False,True],[False,True,True],[False,True,True]] + assert algorithm3(courses, students, students_matrix, mapping_csp) == [[True,False,False],[True,True,False],[False,False,True],[False,True,True],[False,True,True]] + +if __name__=="__main__": + import pytest + pytest.main(args=["fairpy/course_allocation"]) \ No newline at end of file diff --git a/fairpy/course_allocation/demo.py b/fairpy/course_allocation/demo.py new file mode 100644 index 00000000..54adf113 --- /dev/null +++ b/fairpy/course_allocation/demo.py @@ -0,0 +1,89 @@ + +""" +Demo for the 3 course allocation algorithm. Reference: + Eric Budish, Gérard P. Cachon, Judd B. Kessler, Abraham Othman (2017) Course Match: A Large-Scale Implementation of + Approximate Competitive Equilibrium from Equal Incomes for Combinatorial Allocation. Operations Research 65(2):314-336. + https://doi.org/10.1287/opre.2016.1544 +Programmers : Aviv Danino & Ori Ariel +Since: 2023-01 +""" + + +import time +import random +from Course import Course +from Student import Student +from functools import cmp_to_key +import fairpy.adaptors as adaptors +from algorithm1 import algorithm1, random, TabuList, copy, map_price_demand, reset_update_prices, cmp_to_key, reset_students, time +from algorithm2 import algorithm2, csp_mapping, copy, math, cmp_to_key +from algorithm3 import algorithm3, mapping_csp + + +def get_courses_students_matrix(students, courses): + # Initialize the matrix with all False values + matrix = [[False for _ in courses] for _ in students] + + # Loop through each student and mark the courses they are enrolled in as True + for i, student in enumerate(students): + for course in student.courses: + j = courses.index(course) + matrix[i][j] = True + + return matrix + + +if __name__ == '__main__': + # random.seed(3) + start_time = time.time() + # Generate 10 courses + courses = [] + for j in range(10): + name = chr(ord('a') + j) + price = 0 + capacity = 0 + max_capacity = random.randint(3, 10) + courses.append(Course(name=name,price=price, + capacity=capacity, max_capacity=max_capacity)) + + # Generate 40 students + students = [] + for k in range(40): + name = 's' + str(k+1) + budget = 20 + year = random.randint(1, 4) + preferences = random.sample(courses, random.randint(3, 7)) + students.append(Student(name=name, budget=budget, + year=year, courses=courses, preferences=preferences)) + + max_budget = 20 + price_vector1 = adaptors.divide(algorithm1,input=students,courses=courses, max_budget=max_budget, seed=3, time_to=10) + + # price_vector1 = algorithm1( + # courses=courses, max_budget=max_budget, seed=3, students=students, time_to=10) + map_price_demand(price_vector=price_vector1, courses=courses, + max_budget=max_budget, students=students) + p_scalar = (max_budget) + price_vector2 =adaptors.divide(algorithm2,input=[course.price for course in courses],maximum=p_scalar, + eps=0.5, csp_mapping=csp_mapping, students=students, courses=courses) + # price_vector2 = algorithm2(price_vector=[course.price for course in courses], maximum=p_scalar, + # eps=0.5, csp_mapping=csp_mapping, students=students, courses=courses) + print(price_vector2) + # map_price_demand(price_vector=price_vector2,courses=courses,max_budget=max_budget,students=students) + reset_students(students, max_budget) + reset_update_prices(price_vector=price_vector2, courses=courses) + csp_mapping(students, courses) + adaptors.divide(algorithm3,input=courses,students_matrix=get_courses_students_matrix(students, courses),students=students) + # algorithm3(courses=courses, students=students, + # students_matrix=get_courses_students_matrix(students, courses)) + for s in courses: + print(s) + print("***********") + print(time.time() - start_time) + + + + + + + From a070a95e5f11b058a19f37d2990841360ea7eaf7 Mon Sep 17 00:00:00 2001 From: avivdan Date: Wed, 1 Feb 2023 13:25:03 +0200 Subject: [PATCH 2/5] after erel recommendations i fixed demo function to print student and the courses of them, added test to demand function in algorithm1 --- fairpy/course_allocation/Student.py | 3 +++ fairpy/course_allocation/algorithm1.py | 25 ++++++++++++++------- fairpy/course_allocation/algorithm1_test.py | 23 +++++++------------ fairpy/course_allocation/demo.py | 15 +++++-------- 4 files changed, 34 insertions(+), 32 deletions(-) diff --git a/fairpy/course_allocation/Student.py b/fairpy/course_allocation/Student.py index 35c2dcb2..88e490b0 100644 --- a/fairpy/course_allocation/Student.py +++ b/fairpy/course_allocation/Student.py @@ -10,6 +10,9 @@ def __init__(self,name, budget, preferences, courses = [], year=1): def __str__(self) -> str: return self.name + " year: " + str(self.year) + "with budget of (" + str(self.budget) + ") \n" + str(self.courses) + "\n " + str(self.preferences) + + def student_courses(self): + return f"student : {self.name} and courses " + str([c.name for c in self.courses]) def comperator(a, b): if(a.year - b.year == 0): diff --git a/fairpy/course_allocation/algorithm1.py b/fairpy/course_allocation/algorithm1.py index 5121e68c..6ec33ce8 100644 --- a/fairpy/course_allocation/algorithm1.py +++ b/fairpy/course_allocation/algorithm1.py @@ -119,12 +119,20 @@ def get_course(s:Student): #function returning alpha error def alpha_error(price_vector, max_budget, students, courses): map_price_demand(price_vector, max_budget, students, courses) - s = 0 + sum = 0 for course in courses: - s = s + (course.max_capacity - course.capacity)**2 - return s - -# + sum += (course.max_capacity - course.capacity)**2 + return sum + +#return list of string representation of demands +''' + >>> a = Course(name='a', price=0, capacity = 3, max_capacity=5) + >>> b = Course(name='b', price=0, capacity = 4,max_capacity=3) + >>> c = Course(name='c', price=0, capacity = 4,max_capacity=5) + >>> courses = [a, b, c] + >>> lst = get_demand_vector(courses) + >>> ["3/5", "4/3", "4/5"] +''' def get_demand_vector(courses): lst = [] for course in courses: @@ -165,7 +173,7 @@ def algorithm1(students, courses, max_budget:float, time_to:float, seed:int = 3, #for better testing random_state = np.random.RandomState(seed=3) while(time.time() - start_time < time_to): - price_vector = [((random_state.randint(low=1,high=99)/100)*max_budget) for i in range(len(courses))] + price_vector = [((random_state.randint(low=1,high=90)/100)*max_budget) for i in range(len(courses))] search_error = alpha_error(price_vector, max_budget, students, courses) tabu_list = TabuList(5) c = 0 @@ -190,8 +198,9 @@ def algorithm1(students, courses, max_budget:float, time_to:float, seed:int = 3, price_vector = temp #add the demands and not the price vector current_error = alpha_error(price_vector, max_budget, students, courses) - tabu_list.add(get_demand_vector(courses)) - logger.debug((get_demand_vector(courses))) + demand_vector = get_demand_vector(courses) + tabu_list.add(demand_vector) + logger.debug(demand_vector) if(current_error < search_error): search_error = current_error logger.debug("search error is %d", search_error) diff --git a/fairpy/course_allocation/algorithm1_test.py b/fairpy/course_allocation/algorithm1_test.py index 5b06f995..55ba46f0 100644 --- a/fairpy/course_allocation/algorithm1_test.py +++ b/fairpy/course_allocation/algorithm1_test.py @@ -1,4 +1,8 @@ from algorithm1 import algorithm1,Course,Student,random,TabuList,copy,map_price_demand,reset_update_prices,cmp_to_key,reset_students,time + +def pretty_testing(lst:list[float]): + return [round(item, 2) for item in lst] + def test1(): a = Course(name='a', price=0, max_capacity=5) b = Course(name='b', price=0, max_capacity=3) @@ -12,7 +16,7 @@ def test1(): s5 = Student(name='s5', budget=15, preferences=([a, b, c])) students = [s1, s2, s3, s4, s5] max_budget = 15 - assert(algorithm1(students, courses, max_budget, time_to= 1, seed = 3, counter=4) == [4.050000000000001, 1.9500000000000002, 5.55]) + assert(pretty_testing(algorithm1(students, courses, max_budget, time_to= 1, seed = 3, counter=4)) == [4.05, 1.95, 5.55]) def test2(): a = Course(name='a', price=0, max_capacity=3) @@ -27,7 +31,7 @@ def test2(): students = [s1, s2, s3, s4, s5] max_budget = 10 - assert(algorithm1(students, courses, max_budget, time_to= 1, seed = 3, counter=2) == [2.7, 1.3, 3.7]) + assert(pretty_testing(algorithm1(students, courses, max_budget, time_to= 1, seed = 3, counter=2)) == [2.7, 1.3, 3.7]) def test3(): a = Course(name='a', price=0, capacity=0, max_capacity=5) @@ -51,19 +55,8 @@ def test3(): s12 = Student(name='s12', budget=18, year=1, courses=[], preferences=([d,f,e])) students = [s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12] max_budget = 18 - assert(algorithm1(students, courses, max_budget, time_to= 1, seed = 3, counter = 3) == [3.96, 12.6, 9.9, 4.68, 1.08, 4.140000000000001])#7 + assert(pretty_testing(algorithm1(students, courses, max_budget, time_to= 1, seed = 3, counter = 3)) == [3.96, 12.6, 9.9, 4.68, 1.08, 4.14])#7 if __name__=="__main__": import pytest - pytest.main(args=["fairpy/course_allocation"]) - -''' -disclaimer for testing -the tests are not conclusive since computing power may vary -and they using time which work differently in different machines. -that worked on laptop with this specs: -cpu = i5 6200u -os = ubuntu -graphics card = None -and youtube in the background... -''' \ No newline at end of file + pytest.main(args=["fairpy/course_allocation"]) \ No newline at end of file diff --git a/fairpy/course_allocation/demo.py b/fairpy/course_allocation/demo.py index 54adf113..504f7ec2 100644 --- a/fairpy/course_allocation/demo.py +++ b/fairpy/course_allocation/demo.py @@ -48,7 +48,7 @@ def get_courses_students_matrix(students, courses): # Generate 40 students students = [] - for k in range(40): + for k in range(30): name = 's' + str(k+1) budget = 20 year = random.randint(1, 4) @@ -56,7 +56,7 @@ def get_courses_students_matrix(students, courses): students.append(Student(name=name, budget=budget, year=year, courses=courses, preferences=preferences)) - max_budget = 20 + max_budget = 25 price_vector1 = adaptors.divide(algorithm1,input=students,courses=courses, max_budget=max_budget, seed=3, time_to=10) # price_vector1 = algorithm1( @@ -66,18 +66,15 @@ def get_courses_students_matrix(students, courses): p_scalar = (max_budget) price_vector2 =adaptors.divide(algorithm2,input=[course.price for course in courses],maximum=p_scalar, eps=0.5, csp_mapping=csp_mapping, students=students, courses=courses) - # price_vector2 = algorithm2(price_vector=[course.price for course in courses], maximum=p_scalar, - # eps=0.5, csp_mapping=csp_mapping, students=students, courses=courses) + print(price_vector2) - # map_price_demand(price_vector=price_vector2,courses=courses,max_budget=max_budget,students=students) reset_students(students, max_budget) reset_update_prices(price_vector=price_vector2, courses=courses) csp_mapping(students, courses) adaptors.divide(algorithm3,input=courses,students_matrix=get_courses_students_matrix(students, courses),students=students) - # algorithm3(courses=courses, students=students, - # students_matrix=get_courses_students_matrix(students, courses)) - for s in courses: - print(s) + + for s in students: + print(s.student_courses()) print("***********") print(time.time() - start_time) From e2c4019f3ab69ff22475941f3a89122e65ecda82 Mon Sep 17 00:00:00 2001 From: avivdan Date: Wed, 1 Feb 2023 13:28:11 +0200 Subject: [PATCH 3/5] after erel recommendations i fixed demo function to print student and the courses of them, added test to demand function in algorithm1 --- fairpy/course_allocation/algorithm1.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fairpy/course_allocation/algorithm1.py b/fairpy/course_allocation/algorithm1.py index 6ec33ce8..d804b83d 100644 --- a/fairpy/course_allocation/algorithm1.py +++ b/fairpy/course_allocation/algorithm1.py @@ -173,7 +173,7 @@ def algorithm1(students, courses, max_budget:float, time_to:float, seed:int = 3, #for better testing random_state = np.random.RandomState(seed=3) while(time.time() - start_time < time_to): - price_vector = [((random_state.randint(low=1,high=90)/100)*max_budget) for i in range(len(courses))] + price_vector = [((random_state.randint(low=1,high=99)/100)*max_budget) for i in range(len(courses))] search_error = alpha_error(price_vector, max_budget, students, courses) tabu_list = TabuList(5) c = 0 From 88985fad8023fb786178835ebbfa2c0c88ddd964 Mon Sep 17 00:00:00 2001 From: avivdan Date: Thu, 2 Feb 2023 18:18:05 +0200 Subject: [PATCH 4/5] erel asks we given --- fairpy/course_allocation/algorithm1.py | 20 +++++++++++--------- fairpy/course_allocation/algorithm1_test.py | 2 +- fairpy/course_allocation/algorithm2.py | 5 ++++- fairpy/course_allocation/algorithm3.py | 5 ++++- fairpy/course_allocation/demo.py | 8 ++++++++ 5 files changed, 28 insertions(+), 12 deletions(-) diff --git a/fairpy/course_allocation/algorithm1.py b/fairpy/course_allocation/algorithm1.py index d804b83d..1b9774eb 100644 --- a/fairpy/course_allocation/algorithm1.py +++ b/fairpy/course_allocation/algorithm1.py @@ -1,4 +1,7 @@ ''' +Course Match: A Large-Scale Implementation of Approximate Competitive Equilibrium from Equal Incomes for Combinatorial Allocation +Published by:Eric Budish, Gérard P. Cachon, Judd B. Kessler, Abraham Othmanba +Link: https://pubsonline.informs.org/doi/epdf/10.1287/opre.2016.1544 Heuristic search algorithm through price space, originally developed in Othman et al. 2010 designed to give each student a fixed budget at first and try find the must corresponding price vector to approximate competitive equilibrium with lowest clearing error. @@ -15,17 +18,11 @@ import random import copy import doctest -import logging +import logging logger = logging.getLogger(__name__) -console = logging.StreamHandler() -logfile = logging.FileHandler("my_logger1.log", mode="w") -logger.handlers = [console,logfile] -logfile.setFormatter(logging.Formatter('%(asctime)s: %(levelname)s: %(name)s: Line %(lineno)d: %(message)s')) -logger.setLevel(logging.DEBUG) -console.setLevel(logging.INFO) def reset_update_prices(price_vector, courses): ''' @@ -224,7 +221,12 @@ def algorithm1(students, courses, max_budget:float, time_to:float, seed:int = 3, logger.debug(logger_counter) return pStar + +algorithm1.logger = logger + if __name__=="__main__": import pytest - #run algorithm and test of the algorithm - pytest.main(args=["fairpy/course_allocation/algorithm1.py", "fairpy/course_allocation/algorithm1_test.py"]) + #run algorithm and test of the algorithm + print(__file__) + # s1 = __file__[:-3] + "_test.py" + pytest.main(args=[__file__, __file__[:-3]+"_test.py"]) \ No newline at end of file diff --git a/fairpy/course_allocation/algorithm1_test.py b/fairpy/course_allocation/algorithm1_test.py index 55ba46f0..f42da0ea 100644 --- a/fairpy/course_allocation/algorithm1_test.py +++ b/fairpy/course_allocation/algorithm1_test.py @@ -1,6 +1,6 @@ from algorithm1 import algorithm1,Course,Student,random,TabuList,copy,map_price_demand,reset_update_prices,cmp_to_key,reset_students,time -def pretty_testing(lst:list[float]): +def pretty_testing(lst:list): return [round(item, 2) for item in lst] def test1(): diff --git a/fairpy/course_allocation/algorithm2.py b/fairpy/course_allocation/algorithm2.py index c026033c..a346db27 100644 --- a/fairpy/course_allocation/algorithm2.py +++ b/fairpy/course_allocation/algorithm2.py @@ -136,8 +136,11 @@ def algorithm2(price_vector, maximum:int, eps:float, csp_mapping:callable, stude J_hat = max(courses, key=cmp_to_key(Course.comperator)) #15 logger.info(get_demand_vector(courses)) return [c.price for c in courses] #return at the end + +algorithm2.logger = logger + if __name__=="__main__": import pytest # #run algorithm and test of the algorithm - pytest.main(args=["fairpy/course_allocation/algorithm2.py", "fairpy/course_allocation/algorithm2_test.py"]) \ No newline at end of file + pytest.main(args=[__file__, __file__[:-3]+"_test.py"]) \ No newline at end of file diff --git a/fairpy/course_allocation/algorithm3.py b/fairpy/course_allocation/algorithm3.py index ed84157b..0f3b7bcf 100644 --- a/fairpy/course_allocation/algorithm3.py +++ b/fairpy/course_allocation/algorithm3.py @@ -118,7 +118,10 @@ def algorithm3(courses, students, students_matrix,csp_students:callable =mapping logger.debug(logger.debug(print_matrix(students, courses, students_matrix))) return students_matrix +algorithm3.logger = logger + + if __name__=="__main__": import pytest #run algorithm and test of the algorithm - pytest.main(args=["fairpy/course_allocation/algorithm3.py", "fairpy/course_allocation/algorithm3_test.py"]) \ No newline at end of file + pytest.main(args=[__file__, __file__[:-3]+"_test.py"]) \ No newline at end of file diff --git a/fairpy/course_allocation/demo.py b/fairpy/course_allocation/demo.py index 504f7ec2..dd5a3676 100644 --- a/fairpy/course_allocation/demo.py +++ b/fairpy/course_allocation/demo.py @@ -19,6 +19,14 @@ from algorithm2 import algorithm2, csp_mapping, copy, math, cmp_to_key from algorithm3 import algorithm3, mapping_csp +import logging, sys + +algorithm1.logger.addHandler(logging.FileHandler("my_logger1.log", mode="w")) +algorithm1.logger.setLevel(logging.DEBUG) +algorithm2.logger.addHandler(logging.FileHandler("my_logger2.log", mode="w")) +algorithm2.logger.setLevel(logging.DEBUG) +algorithm3.logger.addHandler(logging.FileHandler("my_logger3.log", mode="w")) +algorithm3.logger.setLevel(logging.DEBUG) def get_courses_students_matrix(students, courses): # Initialize the matrix with all False values From 5da495aaf31dd3571e420c357783c2a8d37fc1ba Mon Sep 17 00:00:00 2001 From: avivdan Date: Thu, 2 Feb 2023 18:20:06 +0200 Subject: [PATCH 5/5] erel asks we given --- fairpy/course_allocation/algorithm2.py | 9 ++------- fairpy/course_allocation/algorithm3.py | 8 ++------ 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/fairpy/course_allocation/algorithm2.py b/fairpy/course_allocation/algorithm2.py index a346db27..003c3524 100644 --- a/fairpy/course_allocation/algorithm2.py +++ b/fairpy/course_allocation/algorithm2.py @@ -13,16 +13,11 @@ from Student import Student from functools import cmp_to_key import doctest -import logging + +import logging logger = logging.getLogger(__name__) -console = logging.StreamHandler() -logfile = logging.FileHandler("my_logger2.log", mode="w") -logger.handlers = [console,logfile] -logfile.setFormatter(logging.Formatter('%(asctime)s: %(levelname)s: %(name)s: Line %(lineno)d: %(message)s')) -logger.setLevel(logging.DEBUG) -console.setLevel(logging.WARNING) def csp_mapping(students,courses): ''' diff --git a/fairpy/course_allocation/algorithm3.py b/fairpy/course_allocation/algorithm3.py index 0f3b7bcf..a622e0b4 100644 --- a/fairpy/course_allocation/algorithm3.py +++ b/fairpy/course_allocation/algorithm3.py @@ -11,15 +11,11 @@ from Student import Student from functools import cmp_to_key import doctest + + import logging logger = logging.getLogger(__name__) -console = logging.StreamHandler() # writes to stderr (= cerr) -logfile = logging.FileHandler("my_logger3.log", mode="w") -logger.handlers = [console,logfile] -logfile.setFormatter(logging.Formatter('%(asctime)s: %(levelname)s: %(name)s: Line %(lineno)d: %(message)s')) -logger.setLevel(logging.DEBUG) -console.setLevel(logging.INFO) def mapping_csp(courses, students, helper:dict, students_matrix): '''