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
Exemple #3
0
    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