Ejemplo n.º 1
0
    def solve(self, factors, observed, config, all_bits):
        rvs = list(sorted(factors.keys()))

        # https://cdn.rawgit.com/IBMDecisionOptimization/docplex-doc/master/docs/cp/creating_model.html
        model = CpoModel()
        # model.context.cplex_parameters.threads = cpu_count()
        rv2var = {rv: model.binary_var(str(rv)) for rv in rvs}
        for rv, val in observed.items():
            model.add(rv2var[rv] == bool(val))
        for rv, factor in factors.items():
            ftype = factor.factor_type
            if ftype == 'INV':
                inp = factor.input_rvs[0]
                model.add(rv2var[inp] == 1 - rv2var[rv])
            elif ftype == 'SAME':
                inp = factor.input_rvs[0]
                model.add(rv2var[inp] == rv2var[rv])
            elif ftype == 'AND':
                inp1, inp2 = factor.input_rvs[:2]
                # Linearization of out = inp1 * inp2 for binary variables
                model.add(rv2var[rv] <= rv2var[inp1])
                model.add(rv2var[rv] <= rv2var[inp2])
                model.add(rv2var[rv] >= rv2var[inp1] + rv2var[inp2] - 1)

        # https://cdn.rawgit.com/IBMDecisionOptimization/docplex-doc/master/docs/cp/docplex.cp.parameters.py.html#docplex.cp.parameters.CpoParameters
        params = CpoParameters(Workers=cpu_count())

        model.print_information()
        sol = model.solve(
            agent='local',
            params=params,
            execfile=
            '/Applications/CPLEX_Studio1210/cpoptimizer/bin/x86-64_osx/cpoptimizer'
        )
        solution = {rv: sol.get_value(rv2var[rv]) for rv in rvs}
        return solution
Ejemplo n.º 2
0
def fixed_resource_allocation_model(env: OnlineFlexibleResourceAllocationEnv,
                                    state: EnvState):
    """
    Generate the fixed resource allocation model and then solve it

    Args:
        env: Online Flexible Resource Allocation Env
        state: Environment state

    Returns: Cplex model
    """
    tasks = env._unallocated_tasks
    if state.auction_task:
        tasks.append(state.auction_task)
    fixed_tasks = convert_fixed_task(tasks)
    servers = list(state.server_tasks.keys())

    model = CpoModel('FixedEnv')

    server_task_allocation = {
        (server, task):
        model.binary_var(name=f'{server.name} server - {task.name} task')
        for server in servers for task in fixed_tasks
    }

    for task in fixed_tasks:
        model.add(
            sum(server_task_allocation[(server, task)]
                for server in servers) <= 1)

    for server in servers:
        for time in range(env._total_time_steps):
            time_tasks = [
                task for task in fixed_tasks
                if task.auction_time <= time <= task.deadline
            ]

            model.add(
                sum(
                    min(
                        task.required_storage,
                        task.fixed_loading_speed *
                        (time + 1 - task.auction_time)) *
                    server_task_allocation[(server, task)]
                    for task in time_tasks) <= server.storage_cap)
            model.add(
                sum(task.fixed_compute_speed *
                    server_task_allocation[(server, task)]
                    for task in time_tasks) <= server.computational_cap)
            model.add(
                sum((task.fixed_loading_speed + task.fixed_sending_speed) *
                    server_task_allocation[(server, task)]
                    for task in time_tasks) <= server.bandwidth_cap)

    model.maximize(
        sum(server_task_allocation[(server, task)] for server in servers
            for task in fixed_tasks))

    model_solution = model.solve(log_output=None, TimeLimit=150)
    total_tasks_completed = sum(
        model_solution.get_value(server_task_allocation[(server, task)])
        for server in servers for task in fixed_tasks)

    return total_tasks_completed
def elastic_optimal_solver(tasks: List[ElasticTask], servers: List[Server],
                           time_limit: Optional[int]):
    """
    Elastic Optimal algorithm solver using cplex

    :param tasks: List of tasks
    :param servers: List of servers
    :param time_limit: Time limit for cplex
    :return: the results of the algorithm
    """
    assert time_limit is None or 0 < time_limit, f'Time limit: {time_limit}'

    model = CpoModel('Elastic Optimal')

    # The resource speed variables and the allocation variables
    loading_speeds, compute_speeds, sending_speeds, task_allocation = {}, {}, {}, {}

    # Loop over each task to allocate the variables and add the deadline constraints
    max_bandwidth = max(server.bandwidth_capacity for server in servers)
    max_computation = max(server.computation_capacity for server in servers)
    runnable_tasks = [
        task for task in tasks if any(
            server.can_run_empty(task) for server in servers)
    ]
    for task in runnable_tasks:
        # Check if the task can be run on any server even if empty
        loading_speeds[task] = model.integer_var(
            min=1, max=max_bandwidth - 1, name=f'{task.name} loading speed')
        compute_speeds[task] = model.integer_var(
            min=1, max=max_computation, name=f'{task.name} compute speed')
        sending_speeds[task] = model.integer_var(
            min=1, max=max_bandwidth - 1, name=f'{task.name} sending speed')

        model.add((task.required_storage / loading_speeds[task]) +
                  (task.required_computation / compute_speeds[task]) +
                  (task.required_results_data /
                   sending_speeds[task]) <= task.deadline)

        # The task allocation variables and add the allocation constraint
        for server in servers:
            task_allocation[(task, server)] = model.binary_var(
                name=f'{task.name} Task - {server.name} Server')
        model.add(
            sum(task_allocation[(task, server)] for server in servers) <= 1)

    # For each server, add the resource constraint
    for server in servers:
        model.add(
            sum(task.required_storage * task_allocation[(task, server)]
                for task in runnable_tasks) <= server.available_storage)
        model.add(
            sum(compute_speeds[task] * task_allocation[(task, server)]
                for task in runnable_tasks) <= server.available_computation)
        model.add(
            sum((loading_speeds[task] + sending_speeds[task]) *
                task_allocation[(task, server)]
                for task in runnable_tasks) <= server.available_bandwidth)

    # The optimisation statement
    model.maximize(
        sum(task.value * task_allocation[(task, server)]
            for task in runnable_tasks for server in servers))

    # Solve the cplex model with time limit
    try:
        model_solution: CpoSolveResult = model.solve(log_output=None,
                                                     TimeLimit=time_limit)
    except CpoSolverException as e:
        print(f'Solver Exception: ', e)
        return None

    # Check that it is solved
    if model_solution.get_solve_status() != SOLVE_STATUS_FEASIBLE and \
            model_solution.get_solve_status() != SOLVE_STATUS_OPTIMAL:
        print(f'Optimal solver failed', file=sys.stderr)
        print_model_solution(model_solution)
        print_model(tasks, servers)
        return None

    # Generate the allocation of the tasks and servers
    try:
        for task in runnable_tasks:
            for server in servers:
                if model_solution.get_value(task_allocation[(task, server)]):
                    server_task_allocation(
                        server, task,
                        model_solution.get_value(loading_speeds[task]),
                        model_solution.get_value(compute_speeds[task]),
                        model_solution.get_value(sending_speeds[task]))
                    break

        if abs(model_solution.get_objective_values()[0] -
               sum(t.value for t in tasks if t.running_server)) > 0.1:
            print(
                'Elastic optimal different objective values - '
                f'cplex: {model_solution.get_objective_values()[0]} and '
                f'running task values: {sum(task.value for task in tasks if task.running_server)}',
                file=sys.stderr)
        return model_solution
    except (AssertionError, KeyError) as e:
        print('Error: ', e, file=sys.stderr)
        print_model_solution(model_solution)
Ejemplo n.º 4
0
def optimal_task_price(new_task: ElasticTask, server: Server, time_limit: int, debug_results: bool = False):
    """
    Calculates the task price

    :param new_task: The new task
    :param server: The server
    :param time_limit: Time limit for the cplex
    :param debug_results: debug the results
    :return: task price and task speeds
    """
    assert 0 < time_limit, f'Time limit: {time_limit}'
    assert new_task.required_storage <= server.storage_capacity
    model = CpoModel(f'{new_task.name} Task Price')

    # Add the new task to the list of server allocated tasks
    tasks = server.allocated_tasks + [new_task]

    # Create all of the resource speeds variables
    loading_speeds = {task: model.integer_var(min=1, max=task.loading_ub()) for task in tasks}
    compute_speeds = {task: model.integer_var(min=1, max=task.compute_ub()) for task in tasks}
    sending_speeds = {task: model.integer_var(min=1, max=task.sending_ub()) for task in tasks}

    # Create all of the allocation variables however only on the currently allocated tasks
    allocation = {task: model.binary_var(name=f'{task.name} Task allocated') for task in server.allocated_tasks}

    # Add the deadline constraint
    for task in tasks:
        model.add((task.required_storage / loading_speeds[task]) +
                  (task.required_computation / compute_speeds[task]) +
                  (task.required_results_data / sending_speeds[task]) <= task.deadline)

    # Add the server resource constraints
    model.add(sum(task.required_storage * allocated for task, allocated in allocation.items()) +
              new_task.required_storage <= server.storage_capacity)
    model.add(sum(compute_speeds[task] * allocated for task, allocated in allocation.items()) +
              compute_speeds[new_task] <= server.computation_capacity)
    model.add(sum((loading_speeds[task] + sending_speeds[task]) * allocated for task, allocated in allocation.items()) +
              (loading_speeds[new_task] + sending_speeds[new_task]) <= server.bandwidth_capacity)

    # The optimisation function
    model.maximize(sum(task.price * allocated for task, allocated in allocation.items()))

    # Solve the model with a time limit
    model_solution = model.solve(log_output=None, TimeLimit=time_limit)

    # If the model solution failed then return an infinite price
    if model_solution.get_solve_status() != SOLVE_STATUS_FEASIBLE and \
            model_solution.get_solve_status() != SOLVE_STATUS_OPTIMAL:
        print(f'Cplex model failed - status: {model_solution.get_solve_status()} '
              f'for new {str(new_task)} and {str(server)}')
        return math.inf, {}

    # Get the max server profit that the model finds and calculate the task price through a vcg similar function
    new_server_revenue = model_solution.get_objective_values()[0]
    task_price = max(server.revenue - new_server_revenue + server.price_change, server.initial_price)

    # Get the resource speeds and task allocations
    speeds = {
        task: (model_solution.get_value(loading_speeds[task]),
               model_solution.get_value(compute_speeds[task]),
               model_solution.get_value(sending_speeds[task]),
               model_solution.get_value(allocation[task]) if task in allocation else True)
        for task in tasks
    }

    debug(f'Sever: {server.name} - Prior revenue: {server.revenue}, new revenue: {new_server_revenue}, '
          f'price change: {server.price_change} therefore task price: {task_price}', debug_results)

    return task_price, speeds
def CP_model_subsequence_restricted(params):
##
    NUM_COLORS, NUM_ITEMS, BIN_SIZE, DISCREPANCY, ITEM_SIZES, COLORS, NUM_BINS, Timelimit, SEED = params


    print("*** Running CP Subseq Restricted with instance NUM_COLORS{} NUM_ITEMS{} BIN_SIZE {} DISCREPANCY {} SEED {}".format(
        NUM_COLORS, NUM_ITEMS, BIN_SIZE, DISCREPANCY, SEED))

    mdl = CpoModel()

    ITEMS = [mdl.interval_var(start=[0, sum(ITEM_SIZES)], size=ITEM_SIZES[item], optional=False, name="item_"+str(item)) for item in range(NUM_ITEMS)]
    ITEMS_TO_BINS = [[mdl.interval_var(start=(0, BIN_SIZE-min(ITEM_SIZES)), end=(min(ITEM_SIZES), BIN_SIZE), size=ITEM_SIZES[item], optional=True, name="item{}InBin{}".format(item, bin)) for bin in range(NUM_BINS)] for item in range(NUM_ITEMS)]

    ITEMS_S = mdl.sequence_var(ITEMS, types=COLORS, name="itemSequence")
    BIN_S = [mdl.sequence_var([ITEMS_TO_BINS[item][bin] for item in range(NUM_ITEMS)], types=[COLORS[item] for item in range(NUM_ITEMS)], name="bin{}Sequence".format(bin)) for bin in range(NUM_BINS)]

    # COLORS_S = [[mdl.sequence_var(ITEMS[item][bin] for item in range(NUM_ITEMS) if COLORS[item] == color)] for color in range(NUM_COLORS)]
    BINS_USED = [mdl.binary_var() for bin in range(NUM_BINS)]


    for item in range(NUM_ITEMS):
        # for bin in range(NUM_BINS):
        #     mdl.add_kpi(
        #         mdl.presence_of(ITEMS_TO_BINS[item][bin]),
        #         "item_"+str(item)+" "+str(bin)
        #     )
        mdl.add(
            mdl.sum(
                [mdl.presence_of(
                    ITEMS_TO_BINS[item][bin])
                    for bin in range(NUM_BINS)]
                ) == 1
        )

    # for item in range(NUM_ITEMS):
    #     mdl.add(
    #         mdl.alternative(ITEMS[item], [ITEMS_TO_BINS[item][bin] for bin in range(NUM_BINS)])
    #     )

    for bin in range(NUM_BINS):
        mdl.add(mdl.no_overlap(BIN_S[bin]))


    for item in range(NUM_ITEMS-1):
        mdl.add(
            mdl.end_before_start(ITEMS[item], ITEMS[item+1])
        )

    mdl.add(mdl.no_overlap(ITEMS_S))

    for bin in range(NUM_BINS):
        mdl.add(
            mdl.same_common_subsequence(
                ITEMS_S,
                BIN_S[bin],
            )
        )

    for item in range(NUM_ITEMS):
        for bin in range(NUM_BINS):
            # if (item, bin) in [(17,5), (20, 5), (29, 5)]:
            #     mdl.add_kpi(mdl.type_of_next(BIN_S[bin], ITEMS_TO_BINS[item][bin], lastValue=-1, absentValue=-1), name="typeofnext{}_{}".format(item, bin))
            #
            #     mdl.add_kpi(mdl.type_of_prev(BIN_S[bin], ITEMS_TO_BINS[item][bin], firstValue=-1, absentValue=-1), name="typeofprev{}_{}".format(item, bin))

            mdl.add(
                COLORS[item] != mdl.type_of_next(BIN_S[bin], ITEMS_TO_BINS[item][bin], lastValue=-1, absentValue=-1)
            )

            mdl.add(
                COLORS[item] != mdl.type_of_prev(BIN_S[bin], ITEMS_TO_BINS[item][bin], firstValue=-1, absentValue=-1)
            )

    for bin in range(NUM_BINS):
        mdl.add(
            BINS_USED[bin] ==  mdl.any([
                mdl.presence_of(ITEMS_TO_BINS[item][bin]) for item in range(NUM_ITEMS)
            ])
        )
    for bin in range(NUM_BINS-1):
        mdl.add(
            BINS_USED[bin] >= BINS_USED[bin+1]
        )

    mdl.add(
        mdl.minimize(
            mdl.sum(
                BINS_USED
            )
        )
    )

    # mdl.add(
    #         mdl.sum(
    #             BINS_USED
    #         ) < 48
    # )

    try:

        msol = mdl.solve(TimeLimit = Timelimit)
        mdl._myInstance = (NUM_COLORS, NUM_ITEMS, BIN_SIZE, DISCREPANCY, SEED)

        if msol:

            ITEMS_TO_BINS_S = []
            for bin in range(NUM_BINS):
                b_ = []
                for item in range(NUM_ITEMS):
                    s = msol[ITEMS_TO_BINS[item][bin]]
                    if s:
                        b_.append(s)
                ITEMS_TO_BINS_S.append(b_)
            # return mdl

            seq = msol.get_var_solution(BIN_S[0])

            print(ITEM_SIZES)
            print("CL: ",COLORS)

            Xs = []
            for item in range(NUM_ITEMS):
                for bin in range(NUM_BINS):
                    s = msol[ITEMS_TO_BINS[item][bin]]
                    if s:
                        # print(s)
                        Xs.append(bin)
            print("Xs: ", Xs)

            Xs = np.array(Xs)
            if solution_checker(Xs, params, "restricted_cp_subsequence"):
                write_to_global_cp(msol, mdl, 'restricted_subsequence')

    except Exception as err:
        print(err)
        write_to_global_failed(params, 'restricted_cp_subsequence', is_bad_solution=False)
Ejemplo n.º 6
0
    for j in hours_in_day:
        time = util.Time(i, j, len(time_seg_idx))
        time_seg_idx.append(time)

# -----------------------------------------------------------------------------
# Optimization variables
# -----------------------------------------------------------------------------
# the main loop of making the variables.

mdl = CpoModel()
all_visits = []

# Set admittance variables.
for pat in patient_list:
    name = 'z_' + str(pat.id)
    pat.is_admitted_var = mdl.binary_var(name=name)

# follow indexing system of X_{k,i,j}^{l}: {doc,patient, visit session}, {day}
for doc in doctor_list:
    for pat in patient_list:

        if (doc.skill in pat.treatment_activities):  # then visit!

            # NB! [0:pat.nb_visit_days] slices the visiting days available
            # in the scheuling horizon to exactly the number of days patient
            # should be visited, and forces the selection of the first
            # nb_visit_days. So the patient wont stay in the center for more
            # days than necessary. But maby enforcing the paitent to come
            # exactly in the first nb_visit_days is too limiting? It depends
            # on the top-level assignment sch
            days = policy_data.calendar_visit_days[0:pat.nb_visit_days]
def non_elastic_optimal_solver(tasks: List[NonElasticTask],
                               servers: List[Server],
                               time_limit: Optional[int]):
    """
    Finds the optimal solution

    :param tasks: A list of tasks
    :param servers: A list of servers
    :param time_limit: The time limit to solve with
    :return: The results
    """
    assert time_limit is None or 0 < time_limit, f'Time limit: {time_limit}'

    model = CpoModel('vcg')

    # As no resource speeds then only assign binary variables for the allocation
    allocations = {
        (task, server):
        model.binary_var(name=f'{task.name} task {server.name} server')
        for task in tasks for server in servers
    }

    # Allocation constraint
    for task in tasks:
        model.add(sum(allocations[(task, server)] for server in servers) <= 1)

    # Server resource speeds constraints
    for server in servers:
        model.add(
            sum(task.required_storage * allocations[(task, server)]
                for task in tasks) <= server.available_storage)
        model.add(
            sum(task.compute_speed * allocations[(task, server)]
                for task in tasks) <= server.available_computation)
        model.add(
            sum((task.loading_speed + task.sending_speed) *
                allocations[(task, server)]
                for task in tasks) <= server.available_bandwidth)

    # Optimisation problem
    model.maximize(
        sum(task.value * allocations[(task, server)] for task in tasks
            for server in servers))

    # Solve the cplex model with time limit
    model_solution = model.solve(log_output=None, TimeLimit=time_limit)

    # Check that the model is solved
    if model_solution.get_solve_status() != SOLVE_STATUS_FEASIBLE and \
            model_solution.get_solve_status() != SOLVE_STATUS_OPTIMAL:
        print('Non-elastic optimal failure', file=sys.stderr)
        print_model_solution(model_solution)
        return None

    # Allocate all of the tasks to the servers
    try:
        for task in tasks:
            for server in servers:
                if model_solution.get_value(allocations[(task, server)]):
                    server_task_allocation(server, task, task.loading_speed,
                                           task.compute_speed,
                                           task.sending_speed)
                    break

        if abs(model_solution.get_objective_values()[0] -
               sum(t.value for t in tasks if t.running_server)) > 0.1:
            print(
                'Non-elastic optimal different objective values - '
                f'cplex: {model_solution.get_objective_values()[0]} and '
                f'running task values: {sum(task.value for task in tasks if task.running_server)}',
                file=sys.stderr)
    except (KeyError, AssertionError) as e:
        print('Assertion error in non-elastic optimal algorithm: ',
              e,
              file=sys.stderr)
        print_model_solution(model_solution)
        return None

    return model_solution