def check_and_store(name, tasks, resources, assignment, cost, lower_limit, upper_limit): """ Checks if the results are correct and stores them in the logger. Parameters ---------- name : string Name of the scheduler tasks : int Number of tasks (tau) resources : int Number of resources (R) assignment : np.array(shape=(resources)) Assignment of tasks to resources cost : np.array(shape=(resources, tasks+1)) Cost functions per resource (C) lower_limit : np.array(shape=(resources), dtype=int) Lower limit of number of tasks per resource upper_limit : np.array(shape=(resources), dtype=int) Upper limit of number of tasks per resource """ cmax = support.get_makespan(cost, assignment) if support.check_total_assigned(tasks, assignment) == False: print(f'-- {name} failed to assign {tasks} tasks to' + f' {resources} resources ({np.sum(assignment)}' + f' were assigned).') if support.check_limits(assignment, lower_limit, upper_limit) == False: print(f'-- {name} failed to respect the lower or upper' + f' limits when assigning {tasks} tasks to' + f' {resources} resources.') logger.store(f'{name},{tasks},{resources},{cmax}')
def extended_fedavg(tasks, resources, lower_limit, upper_limit): """ Finds an assignment of tasks to resources using an extended version of FedAvg. Parameters ---------- tasks : int Number of tasks (tau) resources : int Number of resources (R) lower_limit : np.array(shape=(resources), dtype=int) Lower limit of number of tasks per resource upper_limit : np.array(shape=(resources), dtype=int) Upper limit of number of tasks per resource Returns ------- np.array(shape=(resources)) Assignment of tasks to resources """ # divides the tasks as equally as possible mean_tasks = tasks // resources # but it sill may have some leftovers leftover = tasks % resources assignment = np.full(shape=resources, fill_value=mean_tasks) if leftover > 0: # adds the leftover to the first resources assignment[0:leftover] += 1 # extended phase: checks if the assignment is valid if support.check_limits(assignment, lower_limit, upper_limit) == True: return assignment else: # the base assignment was invalid, so we switch to a second alternative # 1. give all resources their lower_limit # 2. iteratively add tasks to the resources with the least tasks that # can still receive more # Assigns lower limit to all resources assignment = np.copy(lower_limit) heap = [] for i in range(resources): # Initializes the heap if assignment[i] < upper_limit[i]: # assignment[i] - mean_tasks: how below a resource is from the # mean. The more negative, the higher the priority heap.append((assignment[i] - mean_tasks, i)) heapq.heapify(heap) # Computes zeta (sum of lower limits) zeta = np.sum(lower_limit) # Iterates assigning the remaining tasks for t in range(zeta + 1, tasks + 1): c, j = heapq.heappop(heap) # Find resource with minimum #tasks assignment[j] += 1 # Assigns task t # Checks if more tasks can be assigned to j if assignment[j] < upper_limit[j]: heapq.heappush(heap, (assignment[j] - mean_tasks, j)) return assignment
def test_check_limits(self): assignment = np.array([4, 4, 4]) lower_limit = np.array([1, 1, 1]) upper_limit = np.array([5, 5, 5]) check = support.check_limits(assignment, lower_limit, upper_limit) self.assertTrue(check) check = support.check_limits(assignment, assignment, assignment) self.assertTrue(check) assignment = np.array([0, 4, 4]) check = support.check_limits(assignment, lower_limit, upper_limit) self.assertFalse(check) assignment = np.array([4, 4, 6]) check = support.check_limits(assignment, lower_limit, upper_limit) self.assertFalse(check) assignment = np.array([0, 4, 6]) check = support.check_limits(assignment, lower_limit, upper_limit) self.assertFalse(check)
def extended_proportional(tasks, resources, cost, k, lower_limit, upper_limit): """ Assigns tasks to resources based on an extended version of the proportional algorithm. Parameters ---------- tasks : int Number of tasks (tau) resources : int Number of resources (R) cost : np.ndarray(shape=(resources, tasks+1)) Cost functions per resource (C) k : int Number of tasks to check the costs and compute a proportion lower_limit : np.array(shape=(resources), dtype=int) Lower limit of number of tasks per resource upper_limit : np.array(shape=(resources), dtype=int) Upper limit of number of tasks per resource Returns ------- np.array(shape=(resources)) Assignment of tasks to resources """ # gets the cost for k tasks on all resources cost_for_k = cost[:, k] # gets the maximum cost to find the proportion max_cost = np.max(cost_for_k) # finds how many tasks each resource could compute with that cost many_tasks = max_cost / cost_for_k # gets the sum of this value to compute the proportion to give # to each resource tasks_sum = np.sum(many_tasks) # splits the tasks using the proportion proportional_tasks = tasks * many_tasks / tasks_sum # transforms them to integers assignment = proportional_tasks.astype(int) # assigns any missing tasks # as the rounding is done at the end, there should be no more than # 'resources' tasks missing total_assigned = np.sum(assignment) leftover = tasks - total_assigned if leftover > 0: # adds the leftover to the first resources assignment[0:leftover] += 1 # extended phase: checks if the assignment is valid if support.check_limits(assignment, lower_limit, upper_limit) == True: return assignment else: # the base assignment was invalid, so we switch to a second alternative # 1. give all resources their lower_limit # 2. iteratively add tasks to the resources that are the farthest from # the desired proportion can still receive more tasks # Assigns lower limit to all resources assignment = np.copy(lower_limit) heap = [] for i in range(resources): # Initializes the heap if assignment[i] < upper_limit[i]: # estimated cost of mapping the next task to i # based on the proportional cost estimated_cost = (assignment[i] + 1) * (cost_for_k[i] / k) heap.append((estimated_cost, i)) heapq.heapify(heap) # Computes zeta (sum of lower limits) zeta = np.sum(lower_limit) # Iterates assigning the remaining tasks for t in range(zeta + 1, tasks + 1): # Finds resource with minimum estimated cost c, j = heapq.heappop(heap) assignment[j] += 1 # Assigns task t # Checks if more tasks can be assigned to j if assignment[j] < upper_limit[j]: estimated_cost = (assignment[j] + 1) * (cost_for_k[j] / k) heapq.heappush(heap, (estimated_cost, j)) return assignment