Beispiel #1
0
def test_add_column(solver: str):
    """Simple test which add columns in a specific way"""
    m = Model()
    x = m.add_var()

    example_constr1 = m.add_constr(x >= 1, "constr1")
    example_constr2 = m.add_constr(x <= 2, "constr2")
    column1 = Column()
    column1.constrs = [example_constr1]
    column1.coeffs = [1]
    second_var = m.add_var("second", column=column1)
    column2 = Column()
    column2.constrs = [example_constr2]
    column2.coeffs = [2]
    m.add_var("third", column=column2)

    vthird = m.vars["third"]
    assert vthird is not None
    assert len(vthird.column.coeffs) == len(vthird.column.constrs)
    assert len(vthird.column.coeffs) == 1

    pconstr2 = m.constrs["constr2"]
    assert vthird.column.constrs[0].name == pconstr2.name
    assert len(example_constr1.expr.expr) == 2
    assert second_var in example_constr1.expr.expr
    assert x in example_constr1.expr.expr
Beispiel #2
0
    def relax_constraints(cls, relaxed_model: mip.Model, slack_dict: dict) -> mip.Model:
        """this method creates a modification of the model `relaxed_model` where all the constraints in the slack_dict are
        modified in order to add the slack values to make the IIS disappear
        Args:
            relaxed_model (mip.Model): model to relax
            slack_dict (dict): pairs {constraint_name: slack_var.value}
        Returns:
            mip.Model: a modification of the original model where all the constraints are modified with the slack values
        """
        for crt_name in slack_dict.keys():
            crt_original = relaxed_model.constr_by_name(crt_name)

            relax_expr = crt_original.expr + slack_dict[crt_name]
            relaxed_model.add_constr(
                relax_expr, name=crt_original.name, priority=crt_original.priority
            )
            relaxed_model.remove(crt_original)  # remove constraint

        return relaxed_model
Beispiel #3
0
def test_cutting_stock(solver: str):
    n = 10  # maximum number of bars
    L = 250  # bar length
    m = 4  # number of requests
    w = [187, 119, 74, 90]  # size of each item
    b = [1, 2, 2, 1]  # demand for each item

    # creating the model
    model = Model(solver_name=solver)
    x = {(i, j): model.add_var(obj=0,
                               var_type=INTEGER,
                               name="x[%d,%d]" % (i, j))
         for i in range(m) for j in range(n)}
    y = {
        j: model.add_var(obj=1, var_type=BINARY, name="y[%d]" % j)
        for j in range(n)
    }

    # constraints
    for i in range(m):
        model.add_constr(xsum(x[i, j] for j in range(n)) >= b[i])
    for j in range(n):
        model.add_constr(xsum(w[i] * x[i, j] for i in range(m)) <= L * y[j])

    # additional constraints to reduce symmetry
    for j in range(1, n):
        model.add_constr(y[j - 1] >= y[j])

    # optimizing the model
    model.optimize()

    # sanity tests
    assert model.status == OptimizationStatus.OPTIMAL
    assert abs(model.objective_value - 3) <= 1e-4
    assert sum(x.x for x in model.vars) >= 5
def build_infeasible_cont_model(num_constraints: int = 10,
                                num_infeasible_sets: int = 20) -> Model:
    # build an infeasible model, based on many redundant constraints
    mdl = Model(name='infeasible_model_continuous')
    var = mdl.add_var(name='var', var_type=CONTINUOUS, lb=-1000, ub=1000)

    for idx, rand_constraint in enumerate(np.linspace(1, 1000,
                                                      num_constraints)):
        crt = mdl.add_constr(var >= rand_constraint,
                             name='lower_bound_{}'.format(idx))
        logger.debug('added {} to the model'.format(crt))

    num_constraint_inf = int(num_infeasible_sets / num_constraints)
    for idx, rand_constraint in enumerate(
            np.linspace(-1000, -1, num_constraint_inf)):
        crt = mdl.add_constr(var <= rand_constraint,
                             name='upper_bound_{}'.format(idx))
        logger.debug('added {} to the model'.format(crt))

    mdl.emphasis = 1  # feasibility
    mdl.preprocess = 1  # -1  automatic, 0  off, 1  on.
    # mdl.pump_passes TODO configure to feasibility emphasis
    return mdl
Beispiel #5
0
class NaiveMIPModel:
    m: Model = None
    customer_facility_map: List[List[Var]] = None
    facility_customer_map: List[List[Var]] = None

    def __init__(self, facilities: List[Facility], customers: List[Customer]):
        self.m = Model('NaiveFacilityMIP', solver_name=CBC)
        self.customer_facility_map = [[self.m.add_var(var_type=BINARY)
                                       for facility in range(len(facilities))] for customer in range(len(customers))]
        self.facility_customer_map = list(zip(*self.customer_facility_map))

        for customer in range(len(customers)):
            self.m.add_constr(xsum(self.customer_facility_map[customer]) == 1)

        for facility in range(len(facilities)):
            self.m.add_constr(xsum([customers[index].demand * variable
                                    for index, variable in enumerate(self.facility_customer_map[facility])]) <=
                              facilities[facility].capacity)

        facility_enabled = [self.m.add_var(var_type=BINARY) for facility in range(len(facilities))]

        for facility in range(len(facilities)):
            for customer in range(len(customers)):
                self.m.add_constr(self.facility_customer_map[facility][customer] <= facility_enabled[facility])

        self.m.objective = xsum([facilities[f].setup_cost * facility_enabled[f] for f in range(len(facilities))]) + \
                           xsum([euclideam_length(facilities[facility].location, customers[customer].location) *
                                 self.facility_customer_map[facility][customer]
                                 for facility in range(len(facilities)) for customer in range(len(customers))])
        self.m.verbose = True
        self.m.max_seconds = 60 * 20

    def optimize(self):
        self.m.optimize()

    def get_best_score(self):
        return self.m.objective_value

    def get_solution(self):
        solution = []
        for customer in self.customer_facility_map:
            for index, facility in enumerate(customer):
                if facility.x >= 0.99:
                    solution.append(index)
                    break
        return solution
Beispiel #6
0
class LP_MRCPSP(MilpSolver):
    def __init__(
        self,
        rcpsp_model: MultiModeRCPSPModel,
        lp_solver=LP_RCPSP_Solver.CBC,
        params_objective_function: ParamsObjectiveFunction = None,
        **kwargs
    ):
        self.rcpsp_model = rcpsp_model
        self.model: Model = None
        self.lp_solver = CBC
        if lp_solver == LP_RCPSP_Solver.GRB:
            self.lp_solver = GRB
        elif lp_solver == LP_RCPSP_Solver.CBC:
            self.lp_solver = CBC
        self.variable_decision = {}
        self.constraints_dict = {}
        self.constraints_dict["lns"] = []
        (
            self.aggreg_from_sol,
            self.aggreg_dict,
            self.params_objective_function,
        ) = build_aggreg_function_and_params_objective(
            problem=self.rcpsp_model,
            params_objective_function=params_objective_function,
        )
        # self.description_variable_description = {}
        # self.description_constraint = {}

    def init_model(self, **args):
        greedy_start = args.get("greedy_start", True)
        start_solution = args.get("start_solution", None)
        verbose = args.get("verbose", False)
        if start_solution is None:
            if greedy_start:
                if verbose:
                    print("Computing greedy solution")
                greedy_solver = PileSolverRCPSP(self.rcpsp_model)
                store_solution = greedy_solver.solve(
                    greedy_choice=GreedyChoice.MOST_SUCCESSORS
                )
                self.start_solution = store_solution.get_best_solution_fit()[0]
                makespan = self.rcpsp_model.evaluate(self.start_solution)["makespan"]
            else:
                if verbose:
                    print("Get dummy solution")
                solution = self.rcpsp_model.get_dummy_solution()
                self.start_solution = solution
                makespan = self.rcpsp_model.evaluate(solution)["makespan"]
        else:
            self.start_solution = start_solution
            makespan = self.rcpsp_model.evaluate(start_solution)["makespan"]

        # p = [0, 3, 2, 5, 4, 2, 3, 4, 2, 4, 6, 0]
        sorted_tasks = sorted(self.rcpsp_model.mode_details.keys())
        p = [
            int(
                max(
                    [
                        self.rcpsp_model.mode_details[key][mode]["duration"]
                        for mode in self.rcpsp_model.mode_details[key]
                    ]
                )
            )
            for key in sorted_tasks
        ]
        # c = [6, 8]
        c = [x for x in self.rcpsp_model.resources.values()]
        renewable = {
            r: self.rcpsp_model.resources[r]
            for r in self.rcpsp_model.resources
            if r not in self.rcpsp_model.non_renewable_resources
        }
        non_renewable = {
            r: self.rcpsp_model.resources[r]
            for r in self.rcpsp_model.non_renewable_resources
        }
        # print('c: ', c)
        # S = [[0, 1], [0, 2], [0, 3], [1, 4], [1, 5], [2, 9], [2, 10], [3, 8], [4, 6],
        #      [4, 7], [5, 9], [5, 10], [6, 8], [6, 9], [7, 8], [8, 11], [9, 11], [10, 11]]
        S = []
        print("successors: ", self.rcpsp_model.successors)
        for task in sorted_tasks:
            for suc in self.rcpsp_model.successors[task]:
                S.append([task, suc])
        # print('S: ', S)
        (R, self.J, self.T) = (range(len(c)), range(len(p)), range(sum(p)))
        # we have a better self.T to limit the number of variables :
        if self.start_solution.rcpsp_schedule_feasible:
            self.T = range(int(makespan + 1))
        # model = Model()
        self.model = Model(sense=MINIMIZE, solver_name=self.lp_solver)
        self.x: Dict[Var] = {}
        last_task = max(self.rcpsp_model.mode_details.keys())
        variable_per_task = {}
        for task in sorted_tasks:
            if task not in variable_per_task:
                variable_per_task[task] = []
            for mode in self.rcpsp_model.mode_details[task]:
                for t in self.T:
                    self.x[(task, mode, t)] = self.model.add_var(
                        name="x({},{}, {})".format(task, mode, t), var_type=BINARY
                    )
                    variable_per_task[task] += [(task, mode, t)]
        self.model.objective = xsum(
            self.x[key] * key[2] for key in variable_per_task[last_task]
        )
        for j in variable_per_task:
            self.model += xsum(self.x[key] for key in variable_per_task[j]) == 1

        if isinstance(self.rcpsp_model, RCPSPModelCalendar):
            renewable_quantity = {r: renewable[r] for r in renewable}
        else:
            renewable_quantity = {r: [renewable[r]] * len(self.T) for r in renewable}

        if isinstance(self.rcpsp_model, RCPSPModelCalendar):
            non_renewable_quantity = {r: non_renewable[r] for r in non_renewable}
        else:
            non_renewable_quantity = {
                r: [non_renewable[r]] * len(self.T) for r in non_renewable
            }

        for (r, t) in product(renewable, self.T):
            self.model.add_constr(
                xsum(
                    int(self.rcpsp_model.mode_details[key[0]][key[1]][r]) * self.x[key]
                    for key in self.x
                    if key[2]
                    <= t
                    < key[2]
                    + int(self.rcpsp_model.mode_details[key[0]][key[1]]["duration"])
                )
                <= renewable_quantity[r][t]
            )
            print(r, t)
        for r in non_renewable:
            self.model.add_constr(
                xsum(
                    int(self.rcpsp_model.mode_details[key[0]][key[1]][r]) * self.x[key]
                    for key in self.x
                )
                <= non_renewable_quantity[r][0]
            )
        durations = {
            j: self.model.add_var(name="duration_" + str(j), var_type=INTEGER)
            for j in variable_per_task
        }
        self.durations = durations
        self.variable_per_task = variable_per_task
        for j in variable_per_task:
            self.model.add_constr(
                xsum(
                    self.rcpsp_model.mode_details[key[0]][key[1]]["duration"]
                    * self.x[key]
                    for key in variable_per_task[j]
                )
                == durations[j]
            )
        for (j, s) in S:
            self.model.add_constr(
                xsum(
                    [key[2] * self.x[key] for key in variable_per_task[s]]
                    + [-key[2] * self.x[key] for key in variable_per_task[j]]
                )
                >= durations[j]
            )

        start = []
        for j in self.start_solution.rcpsp_schedule:
            start_time_j = self.start_solution.rcpsp_schedule[j]["start_time"]
            mode_j = (
                1
                if j == 1 or j == self.rcpsp_model.n_jobs + 2
                else self.start_solution.rcpsp_modes[j - 2]
            )
            start += [
                (
                    self.durations[j],
                    self.rcpsp_model.mode_details[j][mode_j]["duration"],
                )
            ]
            for k in self.variable_per_task[j]:
                task, mode, time = k
                if start_time_j == time and mode == mode_j:
                    start += [(self.x[k], 1)]
                else:
                    start += [(self.x[k], 0)]
        self.model.start = start
        p_s: Union[PartialSolution, None] = args.get("partial_solution", None)
        self.constraints_partial_solutions = []
        if p_s is not None:
            constraints = []
            if p_s.start_times is not None:
                for task in p_s.start_times:
                    constraints += [
                        self.model.add_constr(
                            xsum(
                                [
                                    self.x[k]
                                    for k in self.variable_per_task[task]
                                    if k[2] == p_s.start_times[task]
                                ]
                            )
                            == 1
                        )
                    ]
            if p_s.partial_permutation is not None:
                for t1, t2 in zip(
                    p_s.partial_permutation[:-1], p_s.partial_permutation[1:]
                ):
                    constraints += [
                        self.model.add_constr(
                            xsum(
                                [key[2] * self.x[key] for key in variable_per_task[t1]]
                                + [
                                    -key[2] * self.x[key]
                                    for key in variable_per_task[t2]
                                ]
                            )
                            <= 0
                        )
                    ]
            if p_s.list_partial_order is not None:
                for l in p_s.list_partial_order:
                    for t1, t2 in zip(l[:-1], l[1:]):
                        constraints += [
                            self.model.add_constr(
                                xsum(
                                    [
                                        key[2] * self.x[key]
                                        for key in variable_per_task[t1]
                                    ]
                                    + [
                                        -key[2] * self.x[key]
                                        for key in variable_per_task[t2]
                                    ]
                                )
                                <= 0
                            )
                        ]
            self.constraints_partial_solutions = constraints
            print("Partial solution constraints : ", self.constraints_partial_solutions)

    def retrieve_solutions(self, parameters_milp: ParametersMilp) -> ResultStorage:
        retrieve_all_solution = parameters_milp.retrieve_all_solution
        nb_solutions_max = parameters_milp.n_solutions_max
        nb_solution = min(nb_solutions_max, self.model.num_solutions)
        if not retrieve_all_solution:
            nb_solution = 1
        list_solution_fits = []
        print(nb_solution, " solutions found")
        for s in range(nb_solution):
            rcpsp_schedule = {}
            modes = {}
            objective = self.model.objective_values[s]
            for (task, mode, t) in self.x:
                value = self.x[(task, mode, t)].xi(s)
                if value >= 0.5:
                    rcpsp_schedule[task] = {
                        "start_time": t,
                        "end_time": t
                        + self.rcpsp_model.mode_details[task][mode]["duration"],
                    }
                    modes[task] = mode
            print("Size schedule : ", len(rcpsp_schedule.keys()))
            try:
                modes.pop(1)
                modes.pop(self.rcpsp_model.n_jobs + 2)
                modes_vec = [modes[k] for k in sorted(modes)]
                solution = RCPSPSolution(
                    problem=self.rcpsp_model,
                    rcpsp_schedule=rcpsp_schedule,
                    rcpsp_modes=modes_vec,
                    rcpsp_schedule_feasible=True,
                )
                fit = self.aggreg_from_sol(solution)
                list_solution_fits += [(solution, fit)]
            except:
                pass
        return ResultStorage(
            list_solution_fits=list_solution_fits,
            best_solution=min(list_solution_fits, key=lambda x: x[1])[0],
            mode_optim=self.params_objective_function.sense_function,
        )

    def solve(
        self, parameters_milp: ParametersMilp = ParametersMilp.default(), **kwargs
    ) -> ResultStorage:
        if self.model is None:
            self.init_model(greedy_start=False, **kwargs)
        limit_time_s = parameters_milp.TimeLimit
        self.model.sol_pool_size = parameters_milp.PoolSolutions
        self.model.max_mip_gap_abs = parameters_milp.MIPGapAbs
        self.model.max_mip_gap = parameters_milp.MIPGap
        self.model.optimize(
            max_seconds=limit_time_s, max_solutions=parameters_milp.n_solutions_max
        )
        return self.retrieve_solutions(parameters_milp)
Beispiel #7
0
class LeaderModel:
    m: Model = None
    customer_facility: List[List[int]] = None
    facility_customer: List[List[int]] = None
    facilities: List[Facility] = None
    leaders: List[Facility] = None
    customers: List[Customer] = None
    var_store: Dict[Tuple[int, int], Var] = defaultdict()
    facilities_enabled: List[Var] = []
    NEIGHBOURS = 5
    RADIUS = 1
    CAPACITY_LEADERS = 2
    SETUP_LEADERS = 5

    def __init__(self, facilities: List[Facility], customers: List[Customer]):
        self.facilities = facilities
        self.customers = customers
        self.leaders = self.get_capacity_leaders()
        self.leaders += self.get_setup_leaders()
        self.dedupe_leaders()
        customer_distance_leaders: List[Dict[
            int, int]] = self.get_customer_distance_leaders()

        self.m = Model('LeaderModel', solver_name=CBC)
        self.initialize_customer_facility(customer_distance_leaders)
        self.initialize_facility_customer(customer_distance_leaders)

        self.model_adjustments()
        self.create_model_variables()
        self.add_necessary_constraints()
        self.construct_objective_fn()

    def model_adjustments(self):
        self.m.verbose = True
        self.m.max_seconds = 60 * 10

    def initialize_facility_customer(self, customer_distance_leaders):
        self.facility_customer = [[]] * len(self.facilities)
        for facility in self.facilities:
            facility_vars = []
            found = False
            for leader in self.leaders:
                if leader.index == facility.index:
                    for customer in self.customers:
                        facility_vars.append(customer.index)
                    self.facility_customer[facility.index] = facility_vars
                    found = True
            if found:
                continue
            for customer in self.customers:
                if facility.index in customer_distance_leaders[customer.index]:
                    facility_vars.append(customer.index)
            self.facility_customer[facility.index] = facility_vars

    def construct_objective_fn(self):
        setup_expression = []
        for facility in self.facilities:
            setup_expression.append(0.5 * facility.setup_cost *
                                    self.facilities_enabled[facility.index])

        distance_expressions = []
        for facility in self.facilities:
            for customer_selected in self.facility_customer[facility.index]:
                distance = euclidean_length(
                    facility.location,
                    self.customers[customer_selected].location)
                distance_expressions.append(
                    distance *
                    self.var_store[(customer_selected, facility.index)])

        self.m.objective = xsum(setup_expression) + xsum(distance_expressions)

    def add_necessary_constraints(self):
        for customer in self.customers:
            self.m.add_constr(
                xsum(
                    map(lambda x: self.var_store[(customer.index, x)],
                        self.customer_facility[customer.index])) == 1)
        for facility in self.facilities:
            expressions = []
            for customer_selected in self.facility_customer[facility.index]:
                expressions.append(
                    self.customers[customer_selected].demand *
                    self.var_store[(customer_selected, facility.index)])

                self.m.add_constr(
                    self.var_store[(customer_selected, facility.index)] <=
                    self.facilities_enabled[facility.index])
            self.m.add_constr(xsum(expressions) <= facility.capacity)

    def initialize_customer_facility(self, customer_distance_leaders):
        self.customer_facility = [0] * len(self.customers)
        for customer in self.customers:
            customer_vars = []
            for facility in self.leaders:
                customer_vars.append(facility.index)
            for facility in customer_distance_leaders[customer.index]:
                customer_vars.append(facility)
            self.customer_facility[customer.index] = customer_vars

    def optimize(self):
        self.m.optimize()

    def get_best_score(self):
        return self.m.objective_value

    def get_solution(self):
        solution = []
        for customer in self.customers:
            for facility_selected in self.customer_facility[customer.index]:
                variable = self.var_store[(customer.index, facility_selected)]
                if variable.x >= 0.99:
                    solution.append(facility_selected)
                    break
        return solution

    def get_capacity_leaders(self):
        return list(
            sorted(self.facilities, key=lambda facility: -facility.capacity)
        )[:self.CAPACITY_LEADERS]

    def get_setup_leaders(self):
        return list(
            sorted(
                self.facilities,
                key=lambda facility: facility.setup_cost))[:self.SETUP_LEADERS]

    def get_customer_distance_leaders(self) -> List[Dict[int, int]]:
        facilities_already_selected = set()
        for facility in self.leaders:
            facilities_already_selected.add(facility.index)

        facilities: List[Facility] = list(
            filter(lambda x: x.index not in facilities_already_selected,
                   self.facilities))
        facility_locations = map(lambda x: [x.location.x, x.location.y],
                                 facilities)
        customer_locations = map(lambda x: [x.location.x, x.location.y],
                                 self.customers)

        neigh = NearestNeighbors(n_neighbors=self.NEIGHBOURS,
                                 algorithm="ball_tree")
        neigh.fit(list(facility_locations))
        neighbour_indices = neigh.kneighbors(list(customer_locations),
                                             return_distance=False)

        sorted_facilities_x = list(
            sorted(map(lambda x: (x.location.x, x.index), facilities)))
        sorted_facilities_y = list(
            sorted(map(lambda x: (x.location.y, x.index), facilities)))

        customer_facility_map = [0] * len(self.customers)
        for neighbours, customer in zip(neighbour_indices, self.customers):
            selections = set()
            for neighbour in neighbours:
                selections.add(facilities[neighbour].index)

            customer_facility_map[customer.index] = selections
        return customer_facility_map

    def dedupe_leaders(self):
        seen = set()
        leader_temp = []
        for leader in self.leaders:
            if leader.index in seen:
                continue
            else:
                leader_temp.append(leader)
                seen.add(leader.index)
        self.leaders = leader_temp

    def create_model_variables(self):
        for customer in self.customers:
            facilities_selected = self.customer_facility[customer.index]
            for facility in facilities_selected:
                self.var_store[(customer.index,
                                facility)] = self.m.add_var(var_type=BINARY)

        self.facilities_enabled = [0] * len(self.facilities)
        for facility in self.facilities:
            self.facilities_enabled[facility.index] = self.m.add_var(
                var_type=BINARY)
Beispiel #8
0
from mip import Model, INTEGER, minimize

bus_input = "1789,37,47,1889"
buses = bus_input.split(",")

model = Model()
t = model.add_var(var_type=INTEGER)  # TIME - result

x = []
n_buses = 0
prod_buses = 1
for i in range(len(buses)):
    if buses[i] != "x":
        buses[i] = int(buses[i])
        x.append(model.add_var(var_type=INTEGER))
        model.add_constr(t == buses[i] * x[n_buses] - i)
        n_buses += 1
        prod_buses *= buses[i]

model.objective = minimize(t)

for i in range(len(x)):
    model.add_constr(x[i] >= 1)
model.add_constr(t >= 1)
model.add_constr(t <= prod_buses)

model.optimize()

print('')
print('Objective value: {model.objective_value:.3}'.format(**locals()))
print('Solution: ', end='')
Beispiel #9
0
def test_tsp_cuts(solver: str):
    """tsp related tests"""
    N = ["a", "b", "c", "d", "e", "f", "g"]
    n = len(N)
    i0 = N[0]

    A = {
        ("a", "d"): 56,
        ("d", "a"): 67,
        ("a", "b"): 49,
        ("b", "a"): 50,
        ("d", "b"): 39,
        ("b", "d"): 37,
        ("c", "f"): 35,
        ("f", "c"): 35,
        ("g", "b"): 35,
        ("b", "g"): 25,
        ("a", "c"): 80,
        ("c", "a"): 99,
        ("e", "f"): 20,
        ("f", "e"): 20,
        ("g", "e"): 38,
        ("e", "g"): 49,
        ("g", "f"): 37,
        ("f", "g"): 32,
        ("b", "e"): 21,
        ("e", "b"): 30,
        ("a", "g"): 47,
        ("g", "a"): 68,
        ("d", "c"): 37,
        ("c", "d"): 52,
        ("d", "e"): 15,
        ("e", "d"): 20,
    }

    # input and output arcs per node
    Aout = {n: [a for a in A if a[0] == n] for n in N}
    Ain = {n: [a for a in A if a[1] == n] for n in N}
    m = Model(solver_name=solver)
    m.verbose = 0

    x = {
        a: m.add_var(name="x({},{})".format(a[0], a[1]), var_type=BINARY)
        for a in A
    }

    m.objective = xsum(c * x[a] for a, c in A.items())

    for i in N:
        m += xsum(x[a] for a in Aout[i]) == 1, "out({})".format(i)
        m += xsum(x[a] for a in Ain[i]) == 1, "in({})".format(i)

    # continuous variable to prevent subtours: each
    # city will have a different "identifier" in the planned route
    y = {i: m.add_var(name="y({})".format(i), lb=0.0) for i in N}

    # subtour elimination
    for (i, j) in A:
        if i0 not in [i, j]:
            m.add_constr(y[i] - (n + 1) * x[(i, j)] >= y[j] - n)

    m.cuts_generator = SubTourCutGenerator()

    # tiny model, should be enough to find the optimal
    m.max_seconds = 10
    m.max_nodes = 100
    m.max_solutions = 1000
    m.optimize()

    assert m.status == OptimizationStatus.OPTIMAL  # "mip model status"
    assert abs(m.objective_value - 262) <= TOL  # "mip model objective"
Beispiel #10
0
class InterfaceToSolver:
    """A wrapper for the mip model class, allows interaction with mip using pd.DataFrames."""
    def __init__(self, solver_name='CBC'):
        self.variables = {}
        self.linear_mip_variables = {}

        self.solver_name = solver_name
        if solver_name == 'CBC':
            self.mip_model = Model("market", solver_name=CBC)
            self.linear_mip_model = Model("market", solver_name=CBC)
        elif solver_name == 'GUROBI':
            self.mip_model = Model("market", solver_name=GUROBI)
            self.linear_mip_model = Model("market", solver_name=GUROBI)
        else:
            raise ValueError("Solver '{}' not recognised.")

        self.mip_model.verbose = 0
        self.mip_model.solver.set_mip_gap_abs(1e-10)
        self.mip_model.solver.set_mip_gap(1e-20)
        self.mip_model.lp_method = LP_Method.DUAL

        self.linear_mip_model.verbose = 0
        self.linear_mip_model.solver.set_mip_gap_abs(1e-10)
        self.linear_mip_model.solver.set_mip_gap(1e-20)
        self.linear_mip_model.lp_method = LP_Method.DUAL

    def add_variables(self, decision_variables):
        """Add decision variables to the model.

        Examples
        --------
        >>> decision_variables = pd.DataFrame({
        ...   'variable_id': [0, 1],
        ...   'lower_bound': [0.0, 0.0],
        ...   'upper_bound': [6.0, 1.0],
        ...   'type': ['continuous', 'binary']})

        >>> si = InterfaceToSolver()

        >>> si.add_variables(decision_variables)

        The underlying mip_model should now have 2 variables.

        >>> print(si.mip_model.num_cols)
        2

        The first one should have the following properties.

        >>> print(si.mip_model.var_by_name('0').var_type)
        C

        >>> print(si.mip_model.var_by_name('0').lb)
        0.0

        >>> print(si.mip_model.var_by_name('0').ub)
        6.0

        The second one should have the following properties.

        >>> print(si.mip_model.var_by_name('1').var_type)
        B

        >>> print(si.mip_model.var_by_name('1').lb)
        0.0

        >>> print(si.mip_model.var_by_name('1').ub)
        1.0

        """
        # Create a mapping between the nempy level names for variable types and the mip representation.
        variable_types = {'continuous': CONTINUOUS, 'binary': BINARY}
        # Add each variable to the mip model.
        for variable_id, lower_bound, upper_bound, variable_type in zip(
                list(decision_variables['variable_id']),
                list(decision_variables['lower_bound']),
                list(decision_variables['upper_bound']),
                list(decision_variables['type'])):
            self.variables[variable_id] = self.mip_model.add_var(
                lb=lower_bound,
                ub=upper_bound,
                var_type=variable_types[variable_type],
                name=str(variable_id))

            self.linear_mip_variables[
                variable_id] = self.linear_mip_model.add_var(
                    lb=lower_bound,
                    ub=upper_bound,
                    var_type=variable_types[variable_type],
                    name=str(variable_id))

    def add_sos_type_2(self, sos_variables, sos_id_columns, position_column):
        """Add groups of special ordered sets of type 2 two the mip model.

        Examples
        --------

        >>> decision_variables = pd.DataFrame({
        ...   'variable_id': [0, 1, 2, 3, 4, 5],
        ...   'lower_bound': [0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
        ...   'upper_bound': [5.0, 5.0, 5.0, 5.0, 5.0, 5.0],
        ...   'type': ['continuous', 'continuous', 'continuous',
        ...            'continuous', 'continuous', 'continuous']})

        >>> sos_variables = pd.DataFrame({
        ...   'variable_id': [0, 1, 2, 3, 4, 5],
        ...   'sos_id': ['A', 'A', 'A', 'B', 'B', 'B'],
        ...   'position': [0, 1, 2, 0, 1, 2]})

        >>> si = InterfaceToSolver()

        >>> si.add_variables(decision_variables)

        >>> si.add_sos_type_2(sos_variables, 'sos_id', 'position')

        """

        # Function that adds sets to mip model.
        def add_sos_vars(sos_group):
            self.mip_model.add_sos(
                list(zip(sos_group['vars'], sos_group[position_column])), 2)

        # For each variable_id get the variable object from the mip model
        sos_variables['vars'] = sos_variables['variable_id'].apply(
            lambda x: self.variables[x])
        # Break up the sets based on their id and add them to the model separately.
        sos_variables.groupby(sos_id_columns).apply(add_sos_vars)
        # This is a hack to make sure mip knows there are binary constraints.
        self.mip_model.add_var(var_type=BINARY, obj=0.0)

    def add_sos_type_1(self, sos_variables):
        # Function that adds sets to mip model.
        def add_sos_vars(sos_group):
            self.mip_model.add_sos(
                list(
                    zip(sos_group['vars'],
                        [1.0 for i in range(len(sos_variables['vars']))])), 1)

        # For each variable_id get the variable object from the mip model
        sos_variables['vars'] = sos_variables['variable_id'].apply(
            lambda x: self.variables[x])
        # Break up the sets based on their id and add them to the model separately.
        sos_variables.groupby('sos_id').apply(add_sos_vars)
        # This is a hack to make mip knows there are binary constraints.
        self.mip_model.add_var(var_type=BINARY, obj=0.0)

    def add_objective_function(self, objective_function):
        """Add the objective function to the mip model.

        Examples
        --------

        >>> decision_variables = pd.DataFrame({
        ...   'variable_id': [0, 1, 2, 3, 4, 5],
        ...   'lower_bound': [0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
        ...   'upper_bound': [5.0, 5.0, 5.0, 5.0, 5.0, 5.0],
        ...   'type': ['continuous', 'continuous', 'continuous',
        ...            'continuous', 'continuous', 'continuous']})

        >>> objective_function = pd.DataFrame({
        ...   'variable_id': [0, 1, 3, 4, 5],
        ...   'cost': [1.0, 2.0, -1.0, 5.0, 0.0]})

        >>> si = InterfaceToSolver()

        >>> si.add_variables(decision_variables)

        >>> si.add_objective_function(objective_function)

        >>> print(si.mip_model.var_by_name('0').obj)
        1.0

        >>> print(si.mip_model.var_by_name('5').obj)
        0.0

        """
        objective_function = objective_function.sort_values('variable_id')
        objective_function = objective_function.set_index('variable_id')
        obj = minimize(
            xsum(objective_function['cost'][i] * self.variables[i]
                 for i in list(objective_function.index)))
        self.mip_model.objective = obj
        self.linear_mip_model.objective = obj

    def add_constraints(self, constraints_lhs, constraints_type_and_rhs):
        """Add constraints to the mip model.

        Examples
        --------
        >>> decision_variables = pd.DataFrame({
        ...   'variable_id': [0, 1, 2, 3, 4, 5],
        ...   'lower_bound': [0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
        ...   'upper_bound': [5.0, 5.0, 10.0, 10.0, 5.0, 5.0],
        ...   'type': ['continuous', 'continuous', 'continuous',
        ...            'continuous', 'continuous', 'continuous']})

        >>> constraints_lhs = pd.DataFrame({
        ...   'constraint_id': [1, 1, 2, 2],
        ...   'variable_id': [0, 1, 3, 4],
        ...   'coefficient': [1.0, 0.5, 1.0, 2.0]})

        >>> constraints_type_and_rhs = pd.DataFrame({
        ...   'constraint_id': [1, 2],
        ...   'type': ['<=', '='],
        ...   'rhs': [10.0, 20.0]})

        >>> si = InterfaceToSolver()

        >>> si.add_variables(decision_variables)

        >>> si.add_constraints(constraints_lhs, constraints_type_and_rhs)

        >>> print(si.mip_model.constr_by_name('1'))
        1: +1.0 0 +0.5 1 <= 10.0

        >>> print(si.mip_model.constr_by_name('2'))
        2: +1.0 3 +2.0 4 = 20.0

        """

        constraints_lhs = constraints_lhs.groupby(
            ['constraint_id', 'variable_id'],
            as_index=False).agg({'coefficient': 'sum'})
        rows = constraints_lhs.groupby(['constraint_id'], as_index=False)

        # Make a dictionary so constraint rhs values can be accessed using the constraint id.
        rhs = dict(
            zip(constraints_type_and_rhs['constraint_id'],
                constraints_type_and_rhs['rhs']))
        # Make a dictionary so constraint type can be accessed using the constraint id.
        enq_type = dict(
            zip(constraints_type_and_rhs['constraint_id'],
                constraints_type_and_rhs['type']))
        var_ids = constraints_lhs['variable_id'].to_numpy()
        vars = np.asarray([
            self.variables[k] if k in self.variables.keys() else None
            for k in range(0,
                           max(var_ids) + 1)
        ])
        coefficients = constraints_lhs['coefficient'].to_numpy()
        for row_id, row in rows.indices.items():
            # Use the variable_ids to get mip variable objects present in the constraints
            lhs_variables = vars[var_ids[row]]
            # Use the positions of the non nan values to the lhs coefficients.
            lhs = coefficients[row]
            # Multiply and the variables by their coefficients and sum to create the lhs of the constraint.
            exp = lhs_variables * lhs
            exp = exp.tolist()
            exp = xsum(exp)
            # Add based on inequality type.
            if enq_type[row_id] == '<=':
                new_constraint = exp <= rhs[row_id]
            elif enq_type[row_id] == '>=':
                new_constraint = exp >= rhs[row_id]
            elif enq_type[row_id] == '=':
                new_constraint = exp == rhs[row_id]
            else:
                raise ValueError(
                    "Constraint type not recognised should be one of '<=', '>=' or '='."
                )
            self.mip_model.add_constr(new_constraint, name=str(row_id))
            self.linear_mip_model.add_constr(new_constraint, name=str(row_id))

    def optimize(self):
        """Optimize the mip model.

        If an optimal solution cannot be found and the investigate_infeasibility flag is set to True then remove
        constraints until a feasible solution is found.

        Examples
        --------
        >>> decision_variables = pd.DataFrame({
        ...   'variable_id': [0, 1, 2, 3, 4, 5],
        ...   'lower_bound': [0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
        ...   'upper_bound': [5.0, 5.0, 10.0, 10.0, 5.0, 5.0],
        ...   'type': ['continuous', 'continuous', 'continuous',
        ...            'continuous', 'continuous', 'continuous']})

        >>> constraints_lhs = pd.DataFrame({
        ...   'constraint_id': [1, 1, 2, 2],
        ...   'variable_id': [0, 1, 3, 4],
        ...   'coefficient': [1.0, 0.5, 1.0, 2.0]})

        >>> constraints_type_and_rhs = pd.DataFrame({
        ...   'constraint_id': [1, 2],
        ...   'type': ['<=', '='],
        ...   'rhs': [10.0, 20.0]})

        >>> si = InterfaceToSolver()

        >>> si.add_variables(decision_variables)

        >>> si.add_constraints(constraints_lhs, constraints_type_and_rhs)

        >>> si.optimize()

        >>> decision_variables['value'] = si.get_optimal_values_of_decision_variables(decision_variables)

        >>> print(decision_variables)
           variable_id  lower_bound  upper_bound        type  value
        0            0          0.0          5.0  continuous    0.0
        1            1          0.0          5.0  continuous    0.0
        2            2          0.0         10.0  continuous    0.0
        3            3          0.0         10.0  continuous   10.0
        4            4          0.0          5.0  continuous    5.0
        5            5          0.0          5.0  continuous    0.0
        """
        status = self.mip_model.optimize()
        if status != OptimizationStatus.OPTIMAL:
            # Attempt find constraint causing infeasibility.
            print('Model infeasible attempting to find problem constraint.')
            con_index = find_problem_constraint(self.mip_model)
            print(
                'Couldn\'t find an optimal solution, but removing con {} fixed INFEASIBLITY'
                .format(con_index))
            raise ValueError('Linear program infeasible')

    def get_optimal_values_of_decision_variables(self, variable_definitions):
        """Get the optimal values for each decision variable.

        Examples
        --------

        >>> decision_variables = pd.DataFrame({
        ...   'variable_id': [0, 1, 2, 3, 4, 5],
        ...   'lower_bound': [0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
        ...   'upper_bound': [5.0, 5.0, 10.0, 10.0, 5.0, 5.0],
        ...   'type': ['continuous', 'continuous', 'continuous',
        ...            'continuous', 'continuous', 'continuous']})

        >>> constraints_lhs = pd.DataFrame({
        ...   'constraint_id': [1, 1, 2, 2],
        ...   'variable_id': [0, 1, 3, 4],
        ...   'coefficient': [1.0, 0.5, 1.0, 2.0]})

        >>> constraints_type_and_rhs = pd.DataFrame({
        ...   'constraint_id': [1, 2],
        ...   'type': ['<=', '='],
        ...   'rhs': [10.0, 20.0]})

        >>> si = InterfaceToSolver()

        >>> si.add_variables(decision_variables)

        >>> si.add_constraints(constraints_lhs, constraints_type_and_rhs)

        >>> si.optimize()

        >>> decision_variables['value'] = si.get_optimal_values_of_decision_variables(decision_variables)

        >>> print(decision_variables)
           variable_id  lower_bound  upper_bound        type  value
        0            0          0.0          5.0  continuous    0.0
        1            1          0.0          5.0  continuous    0.0
        2            2          0.0         10.0  continuous    0.0
        3            3          0.0         10.0  continuous   10.0
        4            4          0.0          5.0  continuous    5.0
        5            5          0.0          5.0  continuous    0.0

        """
        values = variable_definitions['variable_id'].apply(
            lambda x: self.mip_model.var_by_name(str(x)).x, self.mip_model)
        return values

    def get_optimal_values_of_decision_variables_lin(self,
                                                     variable_definitions):
        values = variable_definitions['variable_id'].apply(
            lambda x: self.linear_mip_model.var_by_name(str(x)).x,
            self.mip_model)
        return values

    def get_slack_in_constraints(self, constraints_type_and_rhs):
        """Get the slack values in each constraint.

        Examples
        --------

        >>> decision_variables = pd.DataFrame({
        ...   'variable_id': [0, 1, 2, 3, 4, 5],
        ...   'lower_bound': [0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
        ...   'upper_bound': [5.0, 5.0, 10.0, 10.0, 5.0, 5.0],
        ...   'type': ['continuous', 'continuous', 'continuous',
        ...            'continuous', 'continuous', 'continuous']})

        >>> constraints_lhs = pd.DataFrame({
        ...   'constraint_id': [1, 1, 2, 2],
        ...   'variable_id': [0, 1, 3, 4],
        ...   'coefficient': [1.0, 0.5, 1.0, 2.0]})

        >>> constraints_type_and_rhs = pd.DataFrame({
        ...   'constraint_id': [1, 2],
        ...   'type': ['<=', '='],
        ...   'rhs': [10.0, 20.0]})

        >>> si = InterfaceToSolver()

        >>> si.add_variables(decision_variables)

        >>> si.add_constraints(constraints_lhs, constraints_type_and_rhs)

        >>> si.optimize()

        >>> constraints_type_and_rhs['slack'] = si.get_slack_in_constraints(constraints_type_and_rhs)

        >>> print(constraints_type_and_rhs)
           constraint_id type   rhs  slack
        0              1   <=  10.0   10.0
        1              2    =  20.0    0.0

        """
        slack = constraints_type_and_rhs['constraint_id'].apply(
            lambda x: self.mip_model.constr_by_name(str(x)).slack,
            self.mip_model)
        return slack

    def price_constraints(self, constraint_ids_to_price):
        """For each constraint_id find the marginal value of the constraint.

        This is done by incrementing the constraint by a value of 1.0 and re-optimizing the model, the marginal cost
        of the constraint is increase in the objective function value between model runs.

        Examples
        --------

        >>> decision_variables = pd.DataFrame({
        ...   'variable_id': [0, 1, 2, 3, 4, 5],
        ...   'lower_bound': [0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
        ...   'upper_bound': [5.0, 5.0, 10.0, 10.0, 5.0, 5.0],
        ...   'type': ['continuous', 'continuous', 'continuous',
        ...            'continuous', 'continuous', 'continuous']})

        >>> objective_function = pd.DataFrame({
        ...   'variable_id': [0, 1, 2, 3, 4, 5],
        ...   'cost': [1.0, 3.0, 10.0, 8.0, 9.0, 7.0]})

        >>> constraints_lhs = pd.DataFrame({
        ...   'constraint_id': [1, 1, 1, 1],
        ...   'variable_id': [0, 1, 3, 4],
        ...   'coefficient': [1.0, 1.0, 1.0, 1.0]})

        >>> constraints_type_and_rhs = pd.DataFrame({
        ...   'constraint_id': [1],
        ...   'type': ['='],
        ...   'rhs': [20.0]})

        >>> si = InterfaceToSolver()

        >>> si.add_variables(decision_variables)

        >>> si.add_constraints(constraints_lhs, constraints_type_and_rhs)

        >>> si.add_objective_function(objective_function)

        >>> si.optimize()

        >>> si.linear_mip_model.optimize()
        <OptimizationStatus.OPTIMAL: 0>

        >>> prices = si.price_constraints([1])

        >>> print(prices)
        {1: 8.0}

        >>> decision_variables['value'] = si.get_optimal_values_of_decision_variables(decision_variables)

        >>> print(decision_variables)
           variable_id  lower_bound  upper_bound        type  value
        0            0          0.0          5.0  continuous    5.0
        1            1          0.0          5.0  continuous    5.0
        2            2          0.0         10.0  continuous    0.0
        3            3          0.0         10.0  continuous   10.0
        4            4          0.0          5.0  continuous    0.0
        5            5          0.0          5.0  continuous    0.0

        """
        costs = {}
        for id in constraint_ids_to_price:
            costs[id] = self.linear_mip_model.constr_by_name(str(id)).pi
        return costs

    def update_rhs(self, constraint_id, violation_degree):
        constraint = self.linear_mip_model.constr_by_name(str(constraint_id))
        constraint.rhs += violation_degree

    def update_variable_bounds(self, new_bounds):
        for variable_id, lb, ub in zip(new_bounds['variable_id'],
                                       new_bounds['lower_bound'],
                                       new_bounds['upper_bound']):
            self.mip_model.var_by_name(str(variable_id)).lb = lb
            self.mip_model.var_by_name(str(variable_id)).ub = ub

    def disable_variables(self, variables):
        for var_id in variables['variable_id']:
            var = self.linear_mip_model.var_by_name(str(var_id))
            var.lb = 0.0
            var.ub = 0.0
Beispiel #11
0
class AP:
    """
    指派问题求解。

    """
    ap_model = None  # 模型
    dec_vars = None  # decision variable 决策变量
    is_init = False  # 是否初始化
    opt_status = None  # 求解状态

    def __init__(self, profit_matrix: List[int]):
        """
        初始化
        """
        self.profit_matrix = profit_matrix

    def init_model(self):
        # 1. 选择求解器初始化
        self.ap_model = Model(sense=MINIMIZE, solver_name=CBC)
        # 2. 定义决策变量
        self.dec_vars = [[
            self.ap_model.add_var(var_type=BINARY)
            for i in range(len(self.profit_matrix))
        ] for j in range(len(self.profit_matrix))]
        # 3. 定义目标函数
        self.ap_model.objective = minimize(
            xsum(self.dec_vars[i][j] * self.profit_matrix[i][j]
                 for i in range(len(self.profit_matrix))
                 for j in range(len(self.profit_matrix))))
        # 4. 定义约束条件
        for i in range(len(self.profit_matrix)):  # 每行只能有一个1
            self.ap_model.add_constr(
                xsum(self.dec_vars[i][j]
                     for j in range(len(self.profit_matrix))) == 1)
        for j in range(len(self.profit_matrix)):  # 每列只能有一个1
            self.ap_model.add_constr(
                xsum(self.dec_vars[i][j]
                     for i in range(len(self.profit_matrix))) == 1)
        self.is_init = True

    def solve(self, max_seconds: int = 10):
        """
        设定约束时间开始求解
        :param max_seconds:
        :return:
        """
        if not self.is_init:
            self.init_model()
        self.opt_status = self.ap_model.optimize(max_seconds=max_seconds)
        return self.opt_status

    def get_optimum_val(self):
        """
        获取最优解值
        :return:
        """
        if self.opt_status and self.opt_status == OptimizationStatus.OPTIMAL:
            return self.ap_model.objective_value
        else:
            raise Exception('未求得解')

    def get_optimum_sol(self):
        """
        获取最优解变量
        :return:
        """
        if self.opt_status and self.opt_status == OptimizationStatus.OPTIMAL:
            return self.dec_vars
        else:
            raise Exception('未求得解')
Beispiel #12
0
def mip_solver(input_data):
    # Modify this code to run your optimization algorithm

    # parse the input
    lines = input_data.split('\n')

    firstLine = lines[0].split()
    item_count = int(firstLine[0])
    capacity = int(firstLine[1])

    items = []

    for i in range(1, item_count+1):
        line = lines[i]
        parts = line.split()
        items.append(Item(i-1, int(parts[0]), int(parts[1])))

    # a trivial algorithm for filling the knapsack
    # it takes items in-order until the knapsack is full
    value = 0
    weight = 0
    taken = [0]*len(items)
    
    # declare value vector
    val_vec = [item.value for item in items]
    
    # declare weight vector scaled by capacity
    wgt_vec = [item.weight/capacity for item in items]
    
    # start MIP solver
    m = Model(solver_name='GRB')
    print('-Model instatiated!',datetime.datetime.now())
    
    # states search emphasis
    #     - '0' (default) balanced approach
    #     - '1' (feasibility) aggressively searches for feasible solutions
    #     - '2' (optimality) explores search space to tighten dual gap
    m.emphasis = 2
    
    # whenever the distance of the lower and upper bounds is less or 
    # equal max_gap*100%, the search can be finished
    m.max_gap = 0.05
    
    # specifies number of used threads
    # 0 uses solver default configuration, 
    # -1 uses the number of available processing cores 
    # ≥1 uses the specified number of threads. 
    # An increased number of threads may improve the solution time but also increases 
    # the memory consumption. Each thread needs to store a different model instance!
    m.threads = -1
    
    
    # controls the generation of cutting planes
    # cutting planes usually improve the LP relaxation bound but also make the solution time of the LP relaxation larger
    # -1 means automatic
    #  0 disables completely
    #  1 (default) generates cutting planes in a moderate way
    #  2 generates cutting planes aggressively
    #  3 generates even more cutting planes
    m.cuts=-1

    m.preprocess=1
    m.pump_passes=10
    m.sol_pool_size=1
    
    # instantiates taken items variable vector
    taken = [m.add_var(name="it{}".format(i),var_type='B') for i in range(item_count)]
    print('-Variables declared!',datetime.datetime.now())
    
    # instantiates objective function
    m.objective = maximize( xsum( val_vec[i]*taken[i] for i in range(item_count) ) )
    print('-Objective declared!',datetime.datetime.now())
    
    m.add_constr( xsum( wgt_vec[i]*taken[i] for i in range(item_count)) <=1 )
    print('-Contraints processed!',datetime.datetime.now())
    
    #Maximum time in seconds that the search can go on if a feasible solution 
    #is available and it is not being improved
    mssi = 1000 #default = inf
    # specifies maximum number of nodes to be explored in the search tree (default = inf)
    mn = 40000 #default = 1073741824
    # optimize model m within a processing time limit of 'ms' seconds
    ms = 3000 #default = inf
    
    print('-Optimizer start.',datetime.datetime.now())
    # executes the optimization
    #status = m.optimize(max_seconds = ms,max_seconds_same_incumbent = mssi,max_nodes = mn)
    status = m.optimize(max_seconds = ms , max_seconds_same_incumbent = mssi)
    final_obj = m.objective_value
    
    print('Opt. Status:',status)
    print('MIP Sol. Obj.:',final_obj)
    print('Dual Bound:',m.objective_bound)
    print('Dual gap:',m.gap)
    
    sol = [round(it.x) for it in taken]
    value = int(final_obj)
    
    taken = sol
    # prepare the solution in the specified output format
    if status == OptimizationStatus.OPTIMAL:
        output_data = str(value) + ' ' + str(1) + '\n'
        output_data += ' '.join(map(str, taken))
    elif status == OptimizationStatus.FEASIBLE:
        output_data = str(value) + ' ' + str(0) + '\n'
        output_data += ' '.join(map(str, taken))


    if item_count == 10000:
        output_data=greedy(input_data)

    return output_data
Beispiel #13
0
def test_tsp(solver: str):
    """tsp related tests"""
    N = ['a', 'b', 'c', 'd', 'e', 'f', 'g']
    n = len(N)
    i0 = N[0]

    A = {
        ('a', 'd'): 56,
        ('d', 'a'): 67,
        ('a', 'b'): 49,
        ('b', 'a'): 50,
        ('d', 'b'): 39,
        ('b', 'd'): 37,
        ('c', 'f'): 35,
        ('f', 'c'): 35,
        ('g', 'b'): 35,
        ('b', 'g'): 25,
        ('a', 'c'): 80,
        ('c', 'a'): 99,
        ('e', 'f'): 20,
        ('f', 'e'): 20,
        ('g', 'e'): 38,
        ('e', 'g'): 49,
        ('g', 'f'): 37,
        ('f', 'g'): 32,
        ('b', 'e'): 21,
        ('e', 'b'): 30,
        ('a', 'g'): 47,
        ('g', 'a'): 68,
        ('d', 'c'): 37,
        ('c', 'd'): 52,
        ('d', 'e'): 15,
        ('e', 'd'): 20
    }

    # input and output arcs per node
    Aout = {n: [a for a in A if a[0] == n] for n in N}
    Ain = {n: [a for a in A if a[1] == n] for n in N}
    m = Model(solver_name=solver)
    m.verbose = 1

    x = {
        a: m.add_var(name='x({},{})'.format(a[0], a[1]), var_type=BINARY)
        for a in A
    }

    m.objective = xsum(c * x[a] for a, c in A.items())

    for i in N:
        m += xsum(x[a] for a in Aout[i]) == 1, 'out({})'.format(i)
        m += xsum(x[a] for a in Ain[i]) == 1, 'in({})'.format(i)

    # continuous variable to prevent subtours: each
    # city will have a different "identifier" in the planned route
    y = {i: m.add_var(name='y({})'.format(i), lb=0.0) for i in N}

    # subtour elimination
    for (i, j) in A:
        if i0 not in [i, j]:
            m.add_constr(y[i] - (n + 1) * x[(i, j)] >= y[j] - n)

    m.relax()
    m.optimize()

    assert m.status == OptimizationStatus.OPTIMAL  # "lp model status"
    assert abs(m.objective_value - 238.75) <= TOL  # "lp model objective"

    # setting all variables to integer now
    for v in m.vars:
        v.var_type = INTEGER
    m.optimize()

    assert m.status == OptimizationStatus.OPTIMAL  # "mip model status"
    assert abs(m.objective_value - 262) <= TOL  # "mip model objective"
Beispiel #14
0
def milp(data, vocabulary, num_classes, vocab_size):
    """
    Mixed-Integer Linear Programing
    :param data: matrix of one-hot representation of each document per class       shape {num_classes: [None, vocab_size]}
    :param vocabulary: the super vector of the unique words
    :param num_classes: the number of classes
    :param vocab_size: the vocabulary size in terms number of words
    """

    # Create the optimization model
    model = Model()
    print('Optimization model is done!')

    # IMLP Variable: (to optimize)  shape [num_classes, vocab_size]
    V = [[model.add_var(var_type=BINARY) for _ in range(vocab_size)]
         for _ in range(num_classes)]
    print('Optimization variables are done!')

    # Optimization function
    # xsum(V[c][j] for c in range(num_classes) for j in range(vocab_size)) +
    model.objective = minimize(
        xsum(d[j] * V[c_prime][j] for c in range(num_classes) for d in data[c]
             for c_prime in range(num_classes) if c != c_prime
             for j in range(vocab_size)))
    print('Optimization objective function is done!')

    # Constraints
    # Equation (5)
    """
    Each interpretation feature must belong to only one category (Tested)
    """
    for j in range(vocab_size):
        model.add_constr(xsum(V[c][j] for c in range(num_classes)) <= 1)
    print('Optimization subjective equation (5) is done!')

    # Equation (6)
    """
    Each document of class c must have at least one interpretation feature that belong to class c (Tested)
    """
    for c in range(num_classes):
        for d in data[c]:
            model.add_constr(
                xsum(d[j] * V[c][j] for j in range(vocab_size)) >= 1)
    print('Optimization subjective equation (6) is done!')

    # run optimization
    status = model.optimize()
    print('Optimization is over!')

    # print results
    if status == OptimizationStatus.OPTIMAL:
        print('optimal solution cost {} found'.format(model.objective_value))
    elif status == OptimizationStatus.FEASIBLE:
        print('sol.cost {} found, best possible: {}'.format(
            model.objective_value, model))
    elif status == OptimizationStatus.NO_SOLUTION_FOUND:
        print('no feasible solution found, lower bound is: {}'.format(
            model.objective_bound))

    milp_if = dict()
    if status == OptimizationStatus.OPTIMAL or status == OptimizationStatus.FEASIBLE:
        for i in range(num_classes):
            milp_if.setdefault(i, set())
            for j in range(vocab_size):
                if V[i][j].x > 0:
                    milp_if[i].add(vocabulary[j])
    return milp_if
Beispiel #15
0
class LP_RCPSP(MilpSolver):
    def __init__(
        self,
        rcpsp_model: SingleModeRCPSPModel,
        lp_solver=LP_RCPSP_Solver.CBC,
        params_objective_function: ParamsObjectiveFunction = None,
        **kwargs
    ):
        self.rcpsp_model = rcpsp_model
        self.model: Model = None
        self.lp_solver = CBC
        if lp_solver == LP_RCPSP_Solver.GRB:
            self.lp_solver = GRB
        elif lp_solver == LP_RCPSP_Solver.CBC:
            self.lp_solver = CBC
        self.variable_decision = {}
        self.constraints_dict = {}
        self.constraints_dict["lns"] = []
        (
            self.aggreg_from_sol,
            self.aggreg_dict,
            self.params_objective_function,
        ) = build_aggreg_function_and_params_objective(
            problem=self.rcpsp_model,
            params_objective_function=params_objective_function,
        )
        # self.description_variable_description = {}
        # self.description_constraint = {}

    def init_model(self, **args):
        greedy_start = args.get("greedy_start", True)
        start_solution = args.get("start_solution", None)
        verbose = args.get("verbose", False)
        if start_solution is None:
            if greedy_start:
                if verbose:
                    print("Computing greedy solution")
                greedy_solver = PileSolverRCPSP(self.rcpsp_model)
                store_solution = greedy_solver.solve(
                    greedy_choice=GreedyChoice.MOST_SUCCESSORS
                )
                self.start_solution = store_solution.get_best_solution_fit()[0]
                makespan = self.rcpsp_model.evaluate(self.start_solution)["makespan"]
            else:
                if verbose:
                    print("Get dummy solution")
                solution = self.rcpsp_model.get_dummy_solution()
                self.start_solution = solution
                makespan = self.rcpsp_model.evaluate(solution)["makespan"]
        else:
            self.start_solution = start_solution
            makespan = self.rcpsp_model.evaluate(start_solution)["makespan"]
        # p = [0, 3, 2, 5, 4, 2, 3, 4, 2, 4, 6, 0]
        sorted_tasks = sorted(self.rcpsp_model.mode_details.keys())
        print(sorted_tasks)
        p = [
            int(self.rcpsp_model.mode_details[key][1]["duration"])
            for key in sorted_tasks
        ]
        # print('p:', p)

        # u = [[0, 0], [5, 1], [0, 4], [1, 4], [1, 3], [3, 2], [3, 1], [2, 4],
        #      [4, 0], [5, 2], [2, 5], [0, 0]]
        u = []
        for task in sorted_tasks:
            tmp = []
            for r in self.rcpsp_model.resources.keys():
                tmp.append(self.rcpsp_model.mode_details[task][1][r])
            u.append(tmp)
        # print('u: ', u)

        # c = [6, 8]
        c = [x for x in self.rcpsp_model.resources.values()]
        # print('c: ', c)

        # S = [[0, 1], [0, 2], [0, 3], [1, 4], [1, 5], [2, 9], [2, 10], [3, 8], [4, 6],
        #      [4, 7], [5, 9], [5, 10], [6, 8], [6, 9], [7, 8], [8, 11], [9, 11], [10, 11]]
        S = []
        print("successors: ", self.rcpsp_model.successors)
        for task in sorted_tasks:
            for suc in self.rcpsp_model.successors[task]:
                S.append([task - 1, suc - 1])
        # print('S: ', S)
        (R, self.J, self.T) = (range(len(c)), range(len(p)), range(sum(p)))
        # we have a better self.T to limit the number of variables :
        self.T = range(int(makespan + 1))
        # model = Model()
        self.model = Model(sense=MINIMIZE, solver_name=self.lp_solver)

        self.x: List[List[Var]] = [
            [
                self.model.add_var(name="x({},{})".format(j, t), var_type=BINARY)
                for t in self.T
            ]
            for j in self.J
        ]

        self.model.objective = xsum(self.x[len(self.J) - 1][t] * t for t in self.T)

        for j in self.J:
            self.model += xsum(self.x[j][t] for t in self.T) == 1

        for (r, t) in product(R, self.T):
            self.model += (
                xsum(
                    u[j][r] * self.x[j][t2]
                    for j in self.J
                    for t2 in range(max(0, t - p[j] + 1), t + 1)
                )
                <= c[r]
            )

        for (j, s) in S:
            self.model += (
                xsum(t * self.x[s][t] - t * self.x[j][t] for t in self.T) >= p[j]
            )
        start = []
        for j in self.J:
            for t in self.T:
                if self.start_solution.rcpsp_schedule[j + 1]["start_time"] == t:
                    start += [(self.x[j][t], 1)]
                else:
                    start += [(self.x[j][t], 0)]

        self.model.start = start
        p_s: Union[PartialSolution, None] = args.get("partial_solution", None)
        self.constraints_partial_solutions = []
        if p_s is not None:
            constraints = []
            if p_s.start_times is not None:
                for task in p_s.start_times:
                    constraints += [
                        self.model.add_constr(
                            xsum(
                                [
                                    j * self.x[task - 1][j]
                                    for j in range(len(self.x[task - 1]))
                                ]
                            )
                            == p_s.start_times[task]
                        )
                    ]
                    constraints += [
                        self.model.add_constr(
                            self.x[task - 1][p_s.start_times[task]] == 1
                        )
                    ]

            if p_s.partial_permutation is not None:
                for t1, t2 in zip(
                    p_s.partial_permutation[:-1], p_s.partial_permutation[1:]
                ):
                    constraints += [
                        self.model.add_constr(
                            xsum(
                                [
                                    t * self.x[t1 - 1][t] - t * self.x[t2 - 1][t]
                                    for t in self.T
                                ]
                            )
                            <= 0
                        )
                    ]
            if p_s.list_partial_order is not None:
                for l in p_s.list_partial_order:
                    for t1, t2 in zip(l[:-1], l[1:]):
                        constraints += [
                            self.model.add_constr(
                                xsum(
                                    [
                                        t * self.x[t1 - 1][t] - t * self.x[t2 - 1][t]
                                        for t in self.T
                                    ]
                                )
                                <= 0
                            )
                        ]
            self.starts = {}
            for j in range(len(self.x)):
                self.starts[j] = self.model.add_var(
                    name="start_" + str(j), lb=0, ub=makespan
                )
                self.model.add_constr(
                    xsum(t * self.x[j][t] for t in self.T) == self.starts[j]
                )
            if p_s.start_at_end is not None:
                for i, j in p_s.start_at_end:
                    constraints += [
                        self.model.add_constr(
                            self.starts[j - 1] == self.starts[i - 1] + p[i - 1]
                        )
                    ]
            if p_s.start_together is not None:
                for i, j in p_s.start_together:
                    constraints += [
                        self.model.add_constr(self.starts[j - 1] == self.starts[i - 1])
                    ]
            if p_s.start_after_nunit is not None:
                for t1, t2, delta in p_s.start_after_nunit:
                    constraints += [
                        self.model.add_constr(
                            self.starts[t2 - 1] >= self.starts[t1 - 1] + delta
                        )
                    ]
            if p_s.start_at_end_plus_offset is not None:
                for t1, t2, delta in p_s.start_at_end_plus_offset:
                    constraints += [
                        self.model.add_constr(
                            self.starts[t2 - 1]
                            >= self.starts[t1 - 1] + delta + p[t1 - 1]
                        )
                    ]
            self.constraints_partial_solutions = constraints

    def retrieve_solutions(self, parameters_milp: ParametersMilp) -> ResultStorage:
        retrieve_all_solution = parameters_milp.retrieve_all_solution
        nb_solutions_max = parameters_milp.n_solutions_max
        nb_solution = min(nb_solutions_max, self.model.num_solutions)
        if not retrieve_all_solution:
            nb_solution = 1
        list_solution_fits = []
        print(nb_solution, " solutions found")
        for s in range(nb_solution):
            rcpsp_schedule = {}
            objective = self.model.objective_values[s]
            for (j, t) in product(self.J, self.T):
                value = self.x[j][t].xi(s)
                if value >= 0.5:
                    rcpsp_schedule[j + 1] = {
                        "start_time": t,
                        "end_time": t
                        + self.rcpsp_model.mode_details[j + 1][1]["duration"],
                    }
            print("Size schedule : ", len(rcpsp_schedule.keys()))
            try:
                solution = RCPSPSolution(
                    problem=self.rcpsp_model,
                    rcpsp_schedule=rcpsp_schedule,
                    rcpsp_schedule_feasible=True,
                )
                fit = self.aggreg_from_sol(solution)
                list_solution_fits += [(solution, fit)]
            except:
                print("Problem =", rcpsp_schedule, len(rcpsp_schedule))
                pass
        return ResultStorage(
            list_solution_fits=list_solution_fits,
            best_solution=min(list_solution_fits, key=lambda x: x[1])[0],
            mode_optim=self.params_objective_function.sense_function,
        )

    def solve(
        self, parameters_milp: ParametersMilp = ParametersMilp.default(), **kwargs
    ) -> ResultStorage:
        if self.model is None:
            self.init_model()
        limit_time_s = parameters_milp.TimeLimit
        self.model.sol_pool_size = parameters_milp.PoolSolutions
        self.model.max_mip_gap_abs = parameters_milp.MIPGapAbs
        self.model.max_mip_gap = parameters_milp.MIPGap
        self.model.optimize(
            max_seconds=limit_time_s, max_solutions=parameters_milp.n_solutions_max
        )
        return self.retrieve_solutions(parameters_milp)
class LP_MRCPSP_GANTT(MilpSolver):
    def __init__(self,
                 rcpsp_model: RCPSPModelCalendar,
                 rcpsp_solution: RCPSPSolution,
                 lp_solver=MilpSolverName.CBC,
                 **kwargs):
        self.rcpsp_model = rcpsp_model
        self.lp_solver = lp_solver
        self.rcpsp_solution = rcpsp_solution
        self.jobs = sorted(list(self.rcpsp_model.mode_details.keys()))
        self.modes_dict = {
            i + 2: self.rcpsp_solution.rcpsp_modes[i]
            for i in range(len(self.rcpsp_solution.rcpsp_modes))
        }
        self.modes_dict[1] = 1
        self.modes_dict[self.jobs[-1]] = 1
        self.rcpsp_schedule = self.rcpsp_solution.rcpsp_schedule
        # self.set_start_times = set(self.rcpsp_schedule.values())
        self.start_times_dict = {}
        for task in self.rcpsp_schedule:
            t = self.rcpsp_schedule[task]["start_time"]
            if t not in self.start_times_dict:
                self.start_times_dict[t] = set()
            self.start_times_dict[t].add((task, t))
        self.graph_intersection_time = nx.Graph()
        for t in self.jobs:
            self.graph_intersection_time.add_node(t)
        for t in self.jobs:
            intersected_jobs = [
                task for task in self.rcpsp_schedule if intersect(
                    [
                        self.rcpsp_schedule[task]["start_time"],
                        self.rcpsp_schedule[task]["end_time"],
                    ],
                    [
                        self.rcpsp_schedule[t]["start_time"],
                        self.rcpsp_schedule[t]["end_time"],
                    ],
                ) is not None and t != task
            ]
            for tt in intersected_jobs:
                self.graph_intersection_time.add_edge(t, tt)
        cliques = [c for c in nx.find_cliques(self.graph_intersection_time)]
        self.cliques = cliques

    def init_model(self, **args):
        self.model = Model(sense=MINIMIZE,
                           solver_name=map_solver[self.lp_solver])
        self.ressource_id_usage = {
            k:
            {i: {}
             for i in range(len(self.rcpsp_model.calendar_details[k]))}
            for k in self.rcpsp_model.calendar_details.keys()
        }
        variables_per_task = {}
        variables_per_individual = {}
        constraints_ressource_need = {}

        for task in self.jobs:
            start = self.rcpsp_schedule[task]["start_time"]
            end = self.rcpsp_schedule[task]["end_time"]
            for k in self.ressource_id_usage:  # typically worker
                needed_ressource = (self.rcpsp_model.mode_details[task][
                    self.modes_dict[task]][k] > 0)
                if needed_ressource:
                    for individual in self.ressource_id_usage[k]:
                        available = all([
                            self.rcpsp_model.calendar_details[k][individual]
                            [time] for time in range(start, end)
                        ])
                        if available:
                            key_variable = (k, individual, task)
                            self.ressource_id_usage[k][individual][
                                task] = self.model.add_var(
                                    name=str(key_variable),
                                    var_type=BINARY,
                                    obj=random.random(),
                                )
                            if task not in variables_per_task:
                                variables_per_task[task] = set()
                            if k not in variables_per_individual:
                                variables_per_individual[k] = {}
                            if individual not in variables_per_individual[k]:
                                variables_per_individual[k][individual] = set()
                            variables_per_task[task].add(key_variable)
                            variables_per_individual[k][individual].add(
                                key_variable)
                    ressource_needed = self.rcpsp_model.mode_details[task][
                        self.modes_dict[task]][k]
                    if k not in constraints_ressource_need:
                        constraints_ressource_need[k] = {}
                    constraints_ressource_need[k][
                        task] = self.model.add_constr(
                            xsum([
                                self.ressource_id_usage[k][key[1]][key[2]]
                                for key in variables_per_task[task]
                                if key[0] == k
                            ]) == ressource_needed,
                            name="ressource_" + str(k) + "_" + str(task),
                        )
        overlaps_constraints = {}

        for i in range(len(self.cliques)):
            tasks = set(self.cliques[i])
            for k in variables_per_individual:
                for individual in variables_per_individual[k]:
                    keys_variable = [
                        variable
                        for variable in variables_per_individual[k][individual]
                        if variable[2] in tasks
                    ]
                    if len(keys_variable) > 0:
                        overlaps_constraints[(
                            i, k, individual
                        )] = self.model.add_constr(
                            xsum([
                                self.ressource_id_usage[key[0]][key[1]][key[2]]
                                for key in keys_variable
                            ]) <= 1)

    def retrieve_solutions(self,
                           parameters_milp: ParametersMilp) -> ResultStorage:
        retrieve_all_solution = parameters_milp.retrieve_all_solution
        nb_solutions_max = parameters_milp.n_solutions_max
        nb_solution = min(nb_solutions_max, self.model.num_solutions)
        if not retrieve_all_solution:
            nb_solution = 1
        list_solution_fits = []
        print(nb_solution, " solutions found")
        for s in range(nb_solution):
            rcpsp_schedule = {}
            modes = {}
            objective = self.model.objective_values[s]
            resource_id_usage = {
                k: {
                    individual: {
                        task:
                        self.ressource_id_usage[k][individual][task].xi(s)
                        for task in self.ressource_id_usage[k][individual]
                    }
                    for individual in self.ressource_id_usage[k]
                }
                for k in self.ressource_id_usage
            }
            print(resource_id_usage)

    def solve(self,
              parameters_milp: ParametersMilp = ParametersMilp.default(),
              **kwargs) -> ResultStorage:
        if self.model is None:
            self.init_model(greedy_start=False, **kwargs)
        limit_time_s = parameters_milp.TimeLimit
        self.model.sol_pool_size = parameters_milp.PoolSolutions
        self.model.max_mip_gap_abs = parameters_milp.MIPGapAbs
        self.model.max_mip_gap = parameters_milp.MIPGap
        self.model.optimize(max_seconds=limit_time_s,
                            max_solutions=parameters_milp.n_solutions_max)
        return self.retrieve_solutions(parameters_milp)
def tsp_mip_solver(input_data):
    # parse the input
    lines = input_data.split('\n')

    nodeCount = int(lines[0])

    points = []
    for i in range(1, nodeCount + 1):
        line = lines[i]
        parts = line.split()
        points.append(Point(float(parts[0]), float(parts[1])))
    print('Points parsed!')

    # calculate distance matrix
    d_m = [[length(q, p) for q in points] for p in points]
    print('Distance matrix ready!')

    # declare MIP model
    m = Model(solver_name='GRB')
    print('-Model instatiated!', datetime.datetime.now())

    # states search emphasis
    #     - '0' (default) balanced approach
    #     - '1' (feasibility) aggressively searches for feasible solutions
    #     - '2' (optimality) explores search space to tighten dual gap
    m.emphasis = 0

    # whenever the distance of the lower and upper bounds is less or
    # equal max_gap*100%, the search can be finished
    m.max_gap = 0.05

    # specifies number of used threads
    # 0 uses solver default configuration,
    # -1 uses the number of available processing cores
    # ≥1 uses the specified number of threads.
    # An increased number of threads may improve the solution time but also increases
    # the memory consumption. Each thread needs to store a different model instance!
    m.threads = 0

    # controls the generation of cutting planes
    # cutting planes usually improve the LP relaxation bound but also make the solution time of the LP relaxation larger
    # -1 means automatic
    #  0 disables completely
    #  1 (default) generates cutting planes in a moderate way
    #  2 generates cutting planes aggressively
    #  3 generates even more cutting planes
    m.cuts = -1

    m.preprocess = 1
    m.pump_passes = 20
    m.sol_pool_size = 1

    nodes = set(range(nodeCount))

    # instantiate "entering and leaving" variables
    x = [[m.add_var(name="x{}_{}".format(p, q), var_type='B') for q in nodes]
         for p in nodes]
    # instantiate subtour elimination variables
    y = [m.add_var(name="y{}".format(i)) for i in nodes]
    print('-Variables instantiated', datetime.datetime.now())

    # declare objective function
    m.objective = minimize(
        xsum(d_m[i][j] * x[i][j] for i in nodes for j in nodes))

    print('-Objective declared!', datetime.datetime.now())

    # declare constraints
    # leave each city only once
    for i in tqdm(nodes):
        m.add_constr(xsum(x[i][j] for j in nodes - {i}) == 1)

    # enter each city only once
    for i in tqdm(nodes):
        m.add_constr(xsum(x[j][i] for j in nodes - {i}) == 1)

    # subtour elimination constraints
    for (i, j) in tqdm(product(nodes - {0}, nodes - {0})):
        if i != j:
            m.add_constr(y[i] - (nodeCount + 1) * x[i][j] >= y[j] - nodeCount)

    print('-Constraints declared!', datetime.datetime.now())

    #Maximum time in seconds that the search can go on if a feasible solution
    #is available and it is not being improved
    mssi = 1000  #default = inf
    # specifies maximum number of nodes to be explored in the search tree (default = inf)
    mn = 1000000  #default = 1073741824
    # optimize model m within a processing time limit of 'ms' seconds
    ms = 3000  #default = inf

    # executes the optimization
    print('-Optimizer start.', datetime.datetime.now())
    #status = m.optimize(max_seconds = ms,max_seconds_same_incumbent = mssi,max_nodes = mn)
    status = m.optimize(max_seconds=ms, max_seconds_same_incumbent=mssi)

    print('Opt. Status:', status)
    print('MIP Sol. Obj.:', m.objective_value)
    print('Dual Bound:', m.objective_bound)
    print('Dual gap:', m.gap)

    sol = [0]
    c_node = 0
    for j in range(nodeCount - 1):
        for i in range(nodeCount):
            if round(m.var_by_name("x{}_{}".format(c_node, i)).x) != 0:
                sol.append(i)
                c_node = i
                break

    obj = m.objective_value

    # prepare the solution in the specified output format
    if status == OptimizationStatus.OPTIMAL:
        output_data = '%.2f' % obj + ' ' + str(1) + '\n'
        output_data += ' '.join(map(str, sol))
    elif status == OptimizationStatus.FEASIBLE:
        output_data = '%.2f' % obj + ' ' + str(0) + '\n'
        output_data += ' '.join(map(str, sol))

    return output_data
Beispiel #18
0
def test_column_generation(solver: str):
    L = 250  # bar length
    m = 4  # number of requests
    w = [187, 119, 74, 90]  # size of each item
    b = [1, 2, 2, 1]  # demand for each item

    # creating master model
    master = Model(solver_name=solver)

    # creating an initial set of patterns which cut one item per bar
    # to provide the restricted master problem with a feasible solution
    lambdas = [
        master.add_var(obj=1, name="lambda_%d" % (j + 1)) for j in range(m)
    ]

    # creating constraints
    constraints = []
    for i in range(m):
        constraints.append(
            master.add_constr(lambdas[i] >= b[i], name="i_%d" % (i + 1)))

    # creating the pricing problem
    pricing = Model(solver_name=solver)

    # creating pricing variables
    a = [
        pricing.add_var(obj=0, var_type=INTEGER, name="a_%d" % (i + 1))
        for i in range(m)
    ]

    # creating pricing constraint
    pricing += xsum(w[i] * a[i] for i in range(m)) <= L, "bar_length"

    new_vars = True
    while new_vars:
        ##########
        # STEP 1: solving restricted master problem
        ##########
        master.optimize()

        ##########
        # STEP 2: updating pricing objective with dual values from master
        ##########
        pricing += 1 - xsum(constraints[i].pi * a[i] for i in range(m))

        # solving pricing problem
        pricing.optimize()

        ##########
        # STEP 3: adding the new columns (if any is obtained with negative reduced cost)
        ##########
        # checking if columns with negative reduced cost were produced and
        # adding them into the restricted master problem
        if pricing.objective_value < -TOL:
            pattern = [a[i].x for i in range(m)]
            column = Column(constraints, pattern)
            lambdas.append(
                master.add_var(obj=1,
                               column=column,
                               name="lambda_%d" % (len(lambdas) + 1)))

        # if no column with negative reduced cost was produced, then linear
        # relaxation of the restricted master problem is solved
        else:
            new_vars = False

    # printing the solution
    assert len(lambdas) == 8
    assert round(master.objective_value) == 3
Beispiel #19
0
class LP_Solver_MRSCPSP(MilpSolver):
    def __init__(self,
                 rcpsp_model: MS_RCPSPModel,
                 lp_solver: MilpSolverName = MilpSolverName.CBC,
                 params_objective_function: ParamsObjectiveFunction = None,
                 **kwargs):
        self.rcpsp_model = rcpsp_model
        self.model: Model = None
        self.lp_solver = lp_solver
        self.variable_decision = {}
        self.constraints_dict = {}
        self.constraints_dict["lns"] = []
        self.aggreg_from_sol, self.aggreg_dict, self.params_objective_function = \
            build_aggreg_function_and_params_objective(problem=self.rcpsp_model,
                                                       params_objective_function=
                                                       params_objective_function)

    #
    # def init_model(self, **args):
    #     self.model = Model(name="mrcpsp",
    #                        sense=MINIMIZE,
    #                        solver_name=map_solver[self.lp_solver])
    #     sorted_tasks = sorted(self.rcpsp_model.mode_details.keys())
    #     max_duration = sum([int(max([self.rcpsp_model.mode_details[key][mode]['duration']
    #                         for mode in self.rcpsp_model.mode_details[key]]))
    #                         for key in sorted_tasks])+10
    #
    #     # c = [6, 8]
    #     renewable = {r: self.rcpsp_model.resources_availability[r]
    #                  for r in self.rcpsp_model.resources_availability
    #                  if r not in self.rcpsp_model.non_renewable_resources}
    #     non_renewable = {r: self.rcpsp_model.resources_availability[r]
    #                      for r in self.rcpsp_model.non_renewable_resources}
    #     # print('c: ', c)
    #     # S = [[0, 1], [0, 2], [0, 3], [1, 4], [1, 5], [2, 9], [2, 10], [3, 8], [4, 6],
    #     #      [4, 7], [5, 9], [5, 10], [6, 8], [6, 9], [7, 8], [8, 11], [9, 11], [10, 11]]
    #     list_edges = []
    #     print('successors: ', self.rcpsp_model.successors)
    #     for task in sorted_tasks:
    #         for suc in self.rcpsp_model.successors[task]:
    #             list_edges.append([task, suc])
    #     self.x: Dict[Tuple, Var] = {}
    #     self.employee_usage: Dict[Tuple, Var] = {}
    #     self.task_mode: Dict[Tuple, Var] = {}
    #     self.task_mode_without_t: Dict[Tuple, Var] = {}
    #     last_task = max(self.rcpsp_model.mode_details.keys())
    #     all_vars = set()
    #     all_vars_employee = set()
    #     variable_per_task = {}
    #     variable_per_task_mode = {}
    #     variable_per_employee = {}
    #     variable_per_employee_time = {}
    #     variable_employee_usage = {}
    #     tasks_only_ressource = [task for task in self.rcpsp_model.mode_details
    #                             if all([s not in self.rcpsp_model.skills_set
    #                                     or (s in self.rcpsp_model.skills_set
    #                                         and self.rcpsp_model.mode_details[task][mode][s] == 0)
    #                                     for mode in self.rcpsp_model.mode_details[task]
    #                                     for s in self.rcpsp_model.mode_details[task][mode]])]
    #     for employee in self.rcpsp_model.employees:
    #         skills_employee = [skill
    #                            for skill in self.rcpsp_model.employees[employee].dict_skill.keys()
    #                            if self.rcpsp_model.employees[employee].dict_skill[skill].skill_value > 0]
    #         for task in sorted_tasks:
    #             if task in tasks_only_ressource:
    #                 continue
    #             for mode in self.rcpsp_model.mode_details[task]:
    #                 required_skills = [s
    #                                    for s in self.rcpsp_model.mode_details[task][mode]
    #                                    if s in self.rcpsp_model.skills_set
    #                                    and self.rcpsp_model.mode_details[task][mode][s] > 0
    #                                    and s in skills_employee]
    #                 if len(required_skills) == 0:
    #                     # this employee will be useless anyway, pass
    #                     continue
    #                 for t in range(max_duration+1):
    #                     if (task, mode, t) not in self.task_mode:
    #                         if task not in variable_per_task_mode:
    #                             variable_per_task_mode[task] = []
    #                         self.task_mode[(task, mode, t)] = self.model.add_var(name="mode_{},{},{}".format(task,
    #                                                                                                          mode, t),
    #                                                                              var_type=BINARY)
    #                         variable_per_task_mode[task].append((task, mode, t))
    #                     self.x[(task,
    #                             employee,
    #                             mode,
    #                             t)] = self.model.add_var(name="x({},{},{},{})".format(task, employee, mode, t),
    #                                                      var_type=BINARY)
    #                     if task not in variable_per_task:
    #                         variable_per_task[task] = []
    #                     if employee not in variable_per_employee:
    #                         variable_per_employee[employee] = []
    #                     if employee not in variable_per_employee_time:
    #                         variable_per_employee_time[employee] = {}
    #                     if t not in variable_per_employee_time[employee]:
    #                         variable_per_employee_time[employee][t] = []
    #                     variable_per_task[task] += [(task, employee,
    #                                                  mode, t)]
    #                     all_vars.add((task, employee,
    #                                   mode, t))
    #                     variable_per_employee[employee] += [(task, employee,
    #                                                          mode, t)]
    #                     variable_per_employee_time[employee][t] += [(task,
    #                                                                  employee,
    #                                                                  mode,
    #                                                                  t)]
    #                 for s in required_skills:
    #                     if employee not in variable_employee_usage:
    #                         variable_employee_usage[employee] = []
    #                     variable_employee_usage[employee] += [(task, employee, s)]
    #                     all_vars_employee.add((task, employee, s))
    #                     self.employee_usage[(task, employee, s)] = \
    #                         self.model.add_var(name="usage({},{},{})".format(task, employee, s),
    #                                            var_type=BINARY)
    #
    #     employee = "fake-employee"
    #     for task in tasks_only_ressource:
    #         for mode in self.rcpsp_model.mode_details[task]:
    #             for t in range(max_duration + 1):
    #                 if (task, mode, t) not in self.task_mode:
    #                     if task not in variable_per_task_mode:
    #                         variable_per_task_mode[task] = []
    #                     self.task_mode[(task, mode, t)] = self.model.add_var(name="mode_{},{},{}".format(task, mode, t),
    #                                                                          var_type=BINARY)
    #                     variable_per_task_mode[task].append((task, mode, t))
    #                 self.x[(task, employee, mode, t)] = self.model.add_var(name="x({},{},{},{})"
    #                                                                        .format(task, employee, mode, t),
    #                                                                        var_type=BINARY)
    #                 if task not in variable_per_task:
    #                     variable_per_task[task] = []
    #                 if employee not in variable_per_employee:
    #                     variable_per_employee[employee] = []
    #                 if employee not in variable_per_employee_time:
    #                     variable_per_employee_time[employee] = {}
    #                 if t not in variable_per_employee_time[employee]:
    #                     variable_per_employee_time[employee][t] = []
    #                 variable_per_task[task] += [(task, employee,
    #                                              mode, t)]
    #                 variable_per_employee[employee] += [(task, employee,
    #                                                      mode, t)]
    #                 all_vars.add((task, employee,
    #                               mode, t))
    #                 variable_per_employee_time[employee][t] += [(task,
    #                                                              employee,
    #                                                              mode,
    #                                                              t)]
    #     for task, mode, t in self.task_mode:
    #         # all variables in self.x, with same task, mode, time
    #         var_of_interest = [v for v in all_vars if v[0] == task and v[2] == mode and v[3] == t]
    #         for v in var_of_interest:
    #             self.model.add_constr(self.task_mode[(task, mode, t)] >= self.x[v])
    #
    #     tm = set([(x[0], x[1]) for x in self.task_mode])
    #     for t, m in tm:
    #         self.task_mode_without_t[(t, m)] = self.model.add_var(name="mode_{},{}".format(t, m),
    #                                                               var_type=BINARY)
    #     for x in self.task_mode:
    #         self.model.add_constr(self.task_mode_without_t[(x[0], x[1])] >= self.task_mode[x])
    #     for t in sorted_tasks:
    #         self.model.add_constr(xsum(self.task_mode_without_t[x] for x in self.task_mode_without_t
    #                                    if x[0] == t) == 1)
    #
    #     # at least one mode !!!
    #     for task in sorted_tasks:
    #         vars_task_mode = [v for v in self.task_mode if v[0] == task]
    #         self.model.add_constr(xsum(self.task_mode[v] for v in vars_task_mode) >= 1)
    #
    #     self.durations = {j: self.model.add_var(name="duration_" + str(j),
    #                                             var_type=INTEGER)
    #                       for j in variable_per_task}
    #
    #     for task in variable_per_task_mode.keys():
    #         self.model.add_constr(xsum(self.task_mode_without_t[key]
    #                                    * self.rcpsp_model.mode_details[key[0]][key[1]]["duration"]
    #                                    for key in self.task_mode_without_t if key[0] == task)
    #                               == self.durations[task])
    #     for task in variable_per_task_mode.keys():
    #        self.model.add_constr(xsum(self.task_mode[key] for key in variable_per_task_mode[task])
    #                               == self.durations[task]+1)
    #     self.start_time_bin: Dict[Tuple, Var] = {}
    #     self.end_time_bin: Dict[Tuple, Var] = {}
    #
    #     self.start_time: Dict[Tuple, Var] = {}
    #     self.end_time: Dict[Tuple, Var] = {}
    #
    #     for task in variable_per_task:
    #         self.start_time[task] = self.model.add_var(name="start_{}".format(task),
    #                                                             var_type=INTEGER,
    #                                                             lb=0,
    #                                                             ub=max_duration)
    #         self.end_time[task] = self.model.add_var(name="end_{}".format(task),
    #                                                  var_type=INTEGER,
    #                                                  lb=0,
    #                                                  ub=max_duration+10)
    #         self.model.add_constr(self.start_time[task]+self.durations[task]-self.end_time[task] == 0)
    #         for variable in variable_per_task_mode[task]:
    #              time = variable[-1]
    #              # Setting starting date.
    #              self.model.add_constr(self.start_time[task] <= self.task_mode[variable]*time
    #                                    + (1-self.task_mode[variable])*10000)
    #
    #     # constraints on the employees activity :
    #     for employee in variable_per_employee:
    #         if employee == "fake-employee":
    #             # no constraint on this.
    #             continue
    #         for time in variable_per_employee_time[employee]:
    #             # the employee do only one stuff at a time.
    #             self.model.add_constr(xsum(self.x[variable]
    #                                        for variable in variable_per_employee_time[employee][time]) <= 1)
    #             self.model.add_constr(xsum(self.x[variable]
    #                                        for variable in variable_per_employee_time[employee][time])
    #                                   <= 1 if self.rcpsp_model.employees[employee].calendar_employee[time] else 0)
    #             # (makes redundant the precedent one)
    #
    #     for task in variable_per_task:
    #         for employee in variable_per_employee:
    #             if employee == "fake-employee":
    #                 continue
    #             variable_task_employee = [v for v in variable_per_employee[employee]
    #                                       if v[0] == task]
    #             if len(variable_task_employee) > 0:
    #                 variable_skills = [v for v in variable_employee_usage[employee] if v[0] == task]
    #                 skills_dict = {}
    #                 for v in variable_skills:
    #                     if v[2] not in skills_dict:
    #                         skills_dict[v[2]] = []
    #                     skills_dict[v[2]] += [v]
    #                 # for skill in skills_dict:
    #                 #    self.model.add_constr(xsum([self.x[v] for v in variable_task_employee])
    #                 #                          <= self.durations[task]) # Need to check that :/
    #                 for v in variable_task_employee:
    #                     self.model.add_constr(xsum([self.employee_usage[v]
    #                                                 for v in variable_skills]) >= self.x[v])
    #                 self.model.add_constr(xsum(self.x[v] for v in variable_task_employee)
    #                                       >= xsum([self.employee_usage[v]
    #                                                for v in variable_skills]))
    #     # constraints on ressource usage :
    #     from itertools import product
    #
    #     # for (r, t) in product(renewable, range(max_duration+1)):
    #     #     self.model += (xsum(int(self.rcpsp_model.mode_details[key[0]][key[1]][r]) * self.task_mode[key]
    #     #                         for key in self.task_mode)
    #     #                    <= renewable[r][t])
    #     #
    #     # for r in non_renewable:
    #     #     self.model.add_constr(xsum(int(self.rcpsp_model.mode_details[key[0]][key[1]][r]) * self.task_mode[key]
    #     #                                for key in self.task_mode) <= non_renewable[r][0])
    #
    #     # constraint on skill:
    #     done = set()
    #     for (task, mode, time) in self.task_mode:
    #         if (task, mode) not in done:
    #             skills = [s
    #                       for s in self.rcpsp_model.mode_details[task][mode]
    #                       if s in self.rcpsp_model.skills_set
    #                       and self.rcpsp_model.mode_details[task][mode][s]>0]
    #             print("Skilles : ", skills)
    #
    #             if len(skills) > 0:
    #                 for s in skills:
    #                     print("Needs : ", [self.rcpsp_model.mode_details[task][mode][s]
    #                                        for tmt in self.task_mode
    #                                        if tmt[0] == task and tmt[1] == mode])
    #                     variable = [v for v in all_vars_employee if v[0] == task and v[2] == s]
    #                     self.model.add_constr(xsum(self.employee_usage[v]
    #                                                * self.rcpsp_model.employees[v[1]].dict_skill[v[2]].skill_value
    #                                                for v in variable) >=
    #                                           xsum(self.task_mode[tmt]*self.rcpsp_model.mode_details[task][mode][s]
    #                                                for tmt in self.task_mode
    #                                                if tmt[0] == task and tmt[1] == mode))
    #         else:
    #             continue
    #         done.add((task, mode))
    #     # Precedence =
    #     for (j, s) in list_edges:
    #         self.model.add_constr(self.start_time[s]-self.end_time[j]
    #                               >= self.durations[j])
    #     self.model.objective = self.start_time[max(self.start_time)]

    def init_model(self, **args):
        self.model = Model(name="mrcpsp",
                           sense=MINIMIZE,
                           solver_name=map_solver[self.lp_solver])
        sorted_tasks = sorted(self.rcpsp_model.mode_details.keys())
        max_duration = sum([
            int(
                max([
                    self.rcpsp_model.mode_details[key][mode]['duration']
                    for mode in self.rcpsp_model.mode_details[key]
                ])) for key in sorted_tasks
        ]) + 10
        # c = [6, 8]
        renewable = {
            r: self.rcpsp_model.resources_availability[r]
            for r in self.rcpsp_model.resources_availability
            if r not in self.rcpsp_model.non_renewable_resources
        }
        non_renewable = {
            r: self.rcpsp_model.resources_availability[r]
            for r in self.rcpsp_model.non_renewable_resources
        }
        # print('c: ', c)
        # S = [[0, 1], [0, 2], [0, 3], [1, 4], [1, 5], [2, 9], [2, 10], [3, 8], [4, 6],
        #      [4, 7], [5, 9], [5, 10], [6, 8], [6, 9], [7, 8], [8, 11], [9, 11], [10, 11]]
        list_edges = []
        print('successors: ', self.rcpsp_model.successors)
        for task in sorted_tasks:
            for suc in self.rcpsp_model.successors[task]:
                list_edges.append([task, suc])
        times = range(max_duration + 1)
        self.modes = {
            task: {
                mode: self.model.add_var(name="mode_{},{}".format(task, mode),
                                         var_type=BINARY)
                for mode in self.rcpsp_model.mode_details[task]
            }
            for task in self.rcpsp_model.mode_details
        }

        self.start_times = {
            task: {
                mode: {
                    t: self.model.add_var(name="start_{},{},{}".format(
                        task, mode, t),
                                          var_type=BINARY)
                    for t in times
                }
                for mode in self.rcpsp_model.mode_details[task]
            }
            for task in self.rcpsp_model.mode_details
        }
        # you have to choose one starting date :
        for task in self.start_times:
            self.model.add_constr(
                xsum(self.start_times[task][mode][t]
                     for mode in self.start_times[task]
                     for t in self.start_times[task][mode]) == 1)
            for mode in self.modes[task]:
                self.model.add_constr(self.modes[task][mode] == xsum(
                    self.start_times[task][mode][t]
                    for t in self.start_times[task][mode]))
        self.durations = {
            task: self.model.add_var(name="duration_" + str(task),
                                     var_type=INTEGER)
            for task in self.modes
        }
        self.start_times_task = {
            task: self.model.add_var(name="start_time_{}".format(task),
                                     var_type=INTEGER)
            for task in self.start_times
        }
        self.end_times_task = {
            task: self.model.add_var(name="end_time_{}".format(task),
                                     var_type=INTEGER)
            for task in self.start_times
        }

        for task in self.start_times:
            self.model.add_constr(
                xsum(self.start_times[task][mode][t] * t
                     for mode in self.start_times[task]
                     for t in self.start_times[task][mode]) ==
                self.start_times_task[task])
            self.model.add_constr(self.end_times_task[task] -
                                  self.start_times_task[task] -
                                  self.durations[task] == 0)

        for task in self.durations:
            self.model.add_constr(
                xsum(self.rcpsp_model.mode_details[task][mode]["duration"] *
                     self.modes[task][mode]
                     for mode in self.modes[task]) == self.durations[task])
        self.employee_usage = tree()
        task_in_employee_usage = set()
        for employee in self.rcpsp_model.employees:
            skills_employee = [
                skill for skill in
                self.rcpsp_model.employees[employee].dict_skill.keys()
                if self.rcpsp_model.employees[employee].dict_skill[skill].
                skill_value > 0
            ]
            for task in sorted_tasks:
                for mode in self.rcpsp_model.mode_details[task]:
                    required_skills = [
                        s for s in self.rcpsp_model.mode_details[task][mode]
                        if s in self.rcpsp_model.skills_set
                        and self.rcpsp_model.mode_details[task][mode][s] > 0
                        and s in skills_employee
                    ]
                    if len(required_skills) == 0:
                        # this employee will be useless anyway, pass
                        continue
                    for s in required_skills:
                        for t in range(max_duration + 1):
                            self.employee_usage[(employee, task, mode, t, s)] = \
                                self.model.add_var(name="employee_{}{}{}{}{}".format(employee, task, mode, t, s),
                                                   var_type=BINARY)
                            task_in_employee_usage.add(task)
                            self.model.add_constr(self.employee_usage[
                                (employee, task, mode, t, s)] -
                                                  self.modes[task][mode] <= 0)
                            self.model.add_constr(
                                self.employee_usage[
                                    (employee, task, mode, t, s)] -
                                self.start_times[task][mode][t] <= 0)
                            if any(not self.rcpsp_model.employees[employee].
                                   calendar_employee[tt] for tt in range(
                                       t, t +
                                       self.rcpsp_model.mode_details[task]
                                       [mode]["duration"])):
                                # print((employee, task, mode, t, s), "will be 0")
                                # print(self.rcpsp_model.employees[employee].calendar_employee)
                                self.model.add_constr(
                                    self.employee_usage[(employee, task, mode,
                                                         t, s)] == 0)
        employees = set([x[0] for x in self.employee_usage])
        # for emp in employees:
        #     all = [x for x in self.employee_usage if x[0] == emp]
        #     for x1 in all:
        #         for x2 in all:
        #             if x1[0] == x2[0]:
        #                 self.model.add_constr(self.employee_usage[x1]+self.employee_usage[x2] <= 1)
        #             if x1[0] != x2[0]:
        #                 if x1[3]+self.rcpsp_model.mode_details[x1[0]][x1[2]]["duration"]>=x2[3]:
        #                     self.model.add_constr(self.employee_usage[x1] + self.employee_usage[x2] <= 1)
        #     print("Employee ", emp)
        from itertools import product

        # can't work on overlapping tasks.
        for emp, t in product(employees, times):
            self.model.add_constr(
                xsum(self.employee_usage[x] for x in self.employee_usage
                     if x[0] == emp and x[3] <= t < x[3] +
                     int(self.rcpsp_model.mode_details[x[1]][x[2]]["duration"])
                     ) <= 1)
        # ressource usage limit
        for (r, t) in product(renewable, times):
            self.model.add_constr(
                xsum(
                    int(self.rcpsp_model.mode_details[task][mode][r]) *
                    self.start_times[task][mode][time]
                    for task in self.start_times
                    for mode in self.start_times[task]
                    for time in self.start_times[task][mode]
                    if time <= t < time +
                    int(self.rcpsp_model.mode_details[task][mode]["duration"]))
                <= renewable[r][t])
        # for non renewable ones.
        for r in non_renewable:
            self.model.add_constr(
                xsum(
                    int(self.rcpsp_model.mode_details[task][mode][r]) *
                    self.start_times[task][mode][time]
                    for task in self.start_times
                    for mode in self.start_times[task]
                    for time in self.start_times[task][mode]) <=
                non_renewable[r][0])
        for task in self.start_times_task:
            required_skills = [
                (s, mode, self.rcpsp_model.mode_details[task][mode][s])
                for mode in self.rcpsp_model.mode_details[task]
                for s in self.rcpsp_model.mode_details[task][mode]
                if s in self.rcpsp_model.skills_set
                and self.rcpsp_model.mode_details[task][mode][s] > 0
            ]
            skills = set([s[0] for s in required_skills])
            for s in skills:
                employee_usage_keys = [
                    v for v in self.employee_usage
                    if v[1] == task and v[4] == s
                ]
                self.model.add_constr(
                    xsum(self.employee_usage[x] * self.rcpsp_model.employees[
                        x[0]].dict_skill[s].skill_value
                         for x in employee_usage_keys) >=
                    xsum(self.modes[task][mode] *
                         self.rcpsp_model.mode_details[task][mode].get(s, 0)
                         for mode in self.modes[task]))
        for (j, s) in list_edges:
            self.model.add_constr(
                self.start_times_task[s] - self.end_times_task[j] >= 0)
        self.model.objective = self.start_times_task[max(
            self.start_times_task)]

    def retrieve_solutions(self,
                           parameters_milp: ParametersMilp) -> ResultStorage:
        retrieve_all_solution = parameters_milp.retrieve_all_solution
        nb_solutions_max = parameters_milp.n_solutions_max
        nb_solution = min(nb_solutions_max, self.model.num_solutions)
        if not retrieve_all_solution:
            nb_solution = 1
        list_solution_fits = []
        print(nb_solution, " solutions found")
        for s in range(nb_solution):
            rcpsp_schedule = {}
            modes = {}
            objective = self.model.objective_values[s]
            results = {}
            employee_usage = {}
            employee_usage_solution = {}
            for task in self.start_times:
                for mode in self.start_times[task]:
                    for t in self.start_times[task][mode]:
                        value = self.start_times[task][mode][t].xi(s)
                        results[(task, mode, t)] = value
                        if value >= 0.5:
                            rcpsp_schedule[task] = {
                                'start_time':
                                int(t),
                                'end_time':
                                int(t + self.rcpsp_model.mode_details[task]
                                    [mode]['duration'])
                            }
                            modes[task] = mode
            for t in self.employee_usage:
                employee_usage[t] = self.employee_usage[t].xi(s)
                if employee_usage[t] >= 0.5:
                    if t[1] not in employee_usage_solution:
                        employee_usage_solution[t[1]] = {}
                    if t[0] not in employee_usage_solution[t[1]]:
                        employee_usage_solution[t[1]][t[0]] = set()
                    employee_usage_solution[t[1]][t[0]].add(t[4])
                    # (employee, task, mode, time, skill)

            modes = {}
            modes_task = {}
            for t in self.modes:
                for m in self.modes[t]:
                    modes[(t, m)] = self.modes[t][m].xi(s)
                    if modes[(t, m)] >= 0.5:
                        modes_task[t] = m
            durations = {}
            for t in self.durations:
                durations[t] = self.durations[t].xi(s)
            start_time = {}
            for t in self.start_times_task:
                start_time[t] = self.start_times_task[t].xi(s)
            end_time = {}
            for t in self.start_times_task:
                end_time[t] = self.end_times_task[t].xi(s)
            print("Size schedule : ", len(rcpsp_schedule.keys()))
            print("results", "(task, mode, time)",
                  {x: results[x]
                   for x in results if results[x] == 1.})
            print(
                "Employee usage : ", "(employee, task, mode, time, skill)", {
                    x: employee_usage[x]
                    for x in employee_usage if employee_usage[x] == 1.
                })
            print("task mode : ", "(task, mode)",
                  {t: modes[t]
                   for t in modes if modes[t] == 1.})
            print("durations : ", durations)
            print("Start time ", start_time)
            print("End time ", end_time)
            solution = MS_RCPSPSolution(problem=self.rcpsp_model,
                                        modes=modes_task,
                                        schedule=rcpsp_schedule,
                                        employee_usage=employee_usage_solution)
            fit = self.aggreg_from_sol(solution)
            list_solution_fits += [(solution, fit)]
        return ResultStorage(
            list_solution_fits=list_solution_fits,
            mode_optim=self.params_objective_function.sense_function)

    def solve(self, parameters_milp: ParametersMilp, **args) -> ResultStorage:
        if self.model is None:
            import time
            print("Init LP model ")
            t = time.time()
            self.init_model(greedy_start=False)
            print("LP model initialized...in ", time.time() - t, " seconds")
        limit_time_s = parameters_milp.TimeLimit
        self.model.sol_pool_size = parameters_milp.PoolSolutions
        self.model.max_mip_gap_abs = parameters_milp.MIPGapAbs
        self.model.max_mip_gap = parameters_milp.MIPGap
        self.model.optimize(max_seconds=limit_time_s,
                            max_solutions=parameters_milp.n_solutions_max)
        return self.retrieve_solutions(parameters_milp)
Beispiel #20
0
def test_tsp_mipstart(solver: str):
    """tsp related tests"""
    N = ["a", "b", "c", "d", "e", "f", "g"]
    n = len(N)
    i0 = N[0]

    A = {
        ("a", "d"): 56,
        ("d", "a"): 67,
        ("a", "b"): 49,
        ("b", "a"): 50,
        ("d", "b"): 39,
        ("b", "d"): 37,
        ("c", "f"): 35,
        ("f", "c"): 35,
        ("g", "b"): 35,
        ("b", "g"): 25,
        ("a", "c"): 80,
        ("c", "a"): 99,
        ("e", "f"): 20,
        ("f", "e"): 20,
        ("g", "e"): 38,
        ("e", "g"): 49,
        ("g", "f"): 37,
        ("f", "g"): 32,
        ("b", "e"): 21,
        ("e", "b"): 30,
        ("a", "g"): 47,
        ("g", "a"): 68,
        ("d", "c"): 37,
        ("c", "d"): 52,
        ("d", "e"): 15,
        ("e", "d"): 20,
    }

    # input and output arcs per node
    Aout = {n: [a for a in A if a[0] == n] for n in N}
    Ain = {n: [a for a in A if a[1] == n] for n in N}
    m = Model(solver_name=solver)
    m.verbose = 0

    x = {
        a: m.add_var(name="x({},{})".format(a[0], a[1]), var_type=BINARY)
        for a in A
    }

    m.objective = xsum(c * x[a] for a, c in A.items())

    for i in N:
        m += xsum(x[a] for a in Aout[i]) == 1, "out({})".format(i)
        m += xsum(x[a] for a in Ain[i]) == 1, "in({})".format(i)

    # continuous variable to prevent subtours: each
    # city will have a different "identifier" in the planned route
    y = {i: m.add_var(name="y({})".format(i), lb=0.0) for i in N}

    # subtour elimination
    for (i, j) in A:
        if i0 not in [i, j]:
            m.add_constr(y[i] - (n + 1) * x[(i, j)] >= y[j] - n)

    route = ["a", "g", "f", "c", "d", "e", "b", "a"]
    m.start = [(x[route[i - 1], route[i]], 1.0) for i in range(1, len(route))]
    m.optimize()

    assert m.status == OptimizationStatus.OPTIMAL
    assert abs(m.objective_value - 262) <= TOL
m = 4  # number of requests
w = [187, 119, 74, 90]  # size of each item
b = [1, 2, 2, 1]  # demand for each item

# creating the model
model = Model()
x = {(i, j): model.add_var(obj=0, var_type=INTEGER, name="x[%d,%d]" % (i, j))
     for i in range(m) for j in range(n)}
y = {
    j: model.add_var(obj=1, var_type=BINARY, name="y[%d]" % j)
    for j in range(n)
}

# constraints
for i in range(m):
    model.add_constr(xsum(x[i, j] for j in range(n)) >= b[i])
for j in range(n):
    model.add_constr(xsum(w[i] * x[i, j] for i in range(m)) <= L * y[j])

# additional constraints to reduce symmetry
for j in range(1, n):
    model.add_constr(y[j - 1] >= y[j])

# optimizing the model
model.optimize()

# printing the solution
print('')
print('Objective value: {model.objective_value:.3}'.format(**locals()))
print('Solution: ', end='')
for v in model.vars:
Beispiel #22
0
m = 4  # number of requests
w = [187, 119, 74, 90]  # size of each item
b = [1, 2, 2, 1]  # demand for each item

# creating master model
master = Model()

# creating an initial set of patterns which cut one item per bar
# to provide the restricted master problem with a feasible solution
lambdas = [master.add_var(obj=1, name='lambda_%d' % (j + 1)) for j in range(m)]

# creating constraints
constraints = []
for i in range(m):
    constraints.append(
        master.add_constr(lambdas[i] >= b[i], name='i_%d' % (i + 1)))

# creating the pricing problem
pricing = Model()

# creating pricing variables
a = [
    pricing.add_var(obj=0, var_type=INTEGER, name='a_%d' % (i + 1))
    for i in range(m)
]

# creating pricing constraint
pricing += xsum(w[i] * a[i] for i in range(m)) <= L, 'bar_length'

new_vars = True
while new_vars:
Beispiel #23
0
def dispatch(decision_variables, constraints_lhs, constraints_rhs_and_type,
             market_rhs_and_type, constraints_dynamic_rhs_and_type,
             objective_function):
    """Create and solve a linear program, returning prices of the market constraints and decision variables values.

    0. Create the problem instance as a mip-python object instance
    1. Create the decision variables
    2. Create the objective function
    3. Create the constraints
    4. Solve the problem
    5. Retrieve optimal values of each variable
    6. Retrieve the shadow costs of market constraints

    :param decision_variables: dict of DataFrames each with the following columns
        variable_id: int
        lower_bound: float
        upper_bound: float
        type: str one of 'continuous', 'integer' or 'binary'
    :param constraints_lhs_coefficient: dict of DataFrames each with the following columns
        variable_id: int
        constraint_id: int
        coefficient: float
    :param constraints_rhs_and_type: dict of DataFrames each with the following columns
        constraint_id: int
        type: str one of '=', '<=', '>='
        rhs: float
    :param market_constraints_lhs_coefficients: dict of DataFrames each with the following columns
        variable_id: int
        constraint_id: int
        coefficient: float
    :param market_rhs_and_type: dict of DataFrames each with the following columns
        constraint_id: int
        type: str one of '=', '<=', '>='
        rhs: float
    :param objective_function: dict of DataFrames each with the following columns
        variable_id: int
        cost: float
    :return:
        decision_variables: dict of DataFrames each with the following columns
            variable_id: int
            lower_bound: float
            upper_bound: float
            type: str one of 'continuous', 'integer' or 'binary'
            value: float
        market_rhs_and_type: dict of DataFrames each with the following columns
            constraint_id: int
            type: str one of '=', '<=', '>='
            rhs: float
            price: float
    """

    # 0. Create the problem instance as a mip-python object instance
    prob = Model("market")
    prob.verbose = 0

    sos_variables = None
    if 'interpolation_weights' in decision_variables.keys():
        sos_variables = decision_variables['interpolation_weights']

    # 1. Create the decision variables
    decision_variables = pd.concat(decision_variables)
    lp_variables = {}
    variable_types = {'continuous': CONTINUOUS, 'binary': BINARY}
    for variable_id, lower_bound, upper_bound, variable_type in zip(
            list(decision_variables['variable_id']),
            list(decision_variables['lower_bound']),
            list(decision_variables['upper_bound']),
            list(decision_variables['type'])):
        lp_variables[variable_id] = prob.add_var(
            lb=lower_bound,
            ub=upper_bound,
            var_type=variable_types[variable_type],
            name=str(variable_id))

    def add_sos_vars(sos_group):
        prob.add_sos(list(zip(sos_group['vars'], sos_group['loss_segment'])),
                     2)

    if sos_variables is not None:
        sos_variables['vars'] = sos_variables['variable_id'].apply(
            lambda x: lp_variables[x])
        sos_variables.groupby('interconnector').apply(add_sos_vars)

    # 2. Create the objective function
    if len(objective_function) > 0:
        objective_function = pd.concat(list(objective_function.values()))
        objective_function = objective_function.sort_values('variable_id')
        objective_function = objective_function.set_index('variable_id')
        prob.objective = minimize(
            xsum(objective_function['cost'][i] * lp_variables[i]
                 for i in list(objective_function.index)))

    # 3. Create the constraints
    sos_constraints = []
    if len(constraints_rhs_and_type) > 0:
        constraints_rhs_and_type = pd.concat(
            list(constraints_rhs_and_type.values()))
    else:
        constraints_rhs_and_type = pd.DataFrame({})

    if len(constraints_dynamic_rhs_and_type) > 0:
        constraints_dynamic_rhs_and_type = pd.concat(
            list(constraints_dynamic_rhs_and_type.values()))
        constraints_dynamic_rhs_and_type['rhs'] = constraints_dynamic_rhs_and_type.\
            apply(lambda x: lp_variables[x['rhs_variable_id']], axis=1)
        rhs_and_type = pd.concat([constraints_rhs_and_type] +
                                 list(market_rhs_and_type.values()) +
                                 [constraints_dynamic_rhs_and_type])
    else:
        rhs_and_type = pd.concat([constraints_rhs_and_type] +
                                 list(market_rhs_and_type.values()))

    constraint_matrix = constraints_lhs.pivot('constraint_id', 'variable_id',
                                              'coefficient')
    constraint_matrix = constraint_matrix.sort_index(axis=1)
    constraint_matrix = constraint_matrix.sort_index()
    column_ids = np.asarray(constraint_matrix.columns)
    row_ids = np.asarray(constraint_matrix.index)
    constraint_matrix_np = np.asarray(constraint_matrix)

    # if len(constraint_matrix.columns) != max(decision_variables['variable_id']) + 1:
    #     raise check.ModelBuildError("Not all variables used in constraint matrix")

    rhs = dict(zip(rhs_and_type['constraint_id'], rhs_and_type['rhs']))
    enq_type = dict(zip(rhs_and_type['constraint_id'], rhs_and_type['type']))
    #var_list = np.asarray([lp_variables[k] for k in sorted(list(lp_variables))])
    #t0 = time()
    for row, id in zip(constraint_matrix_np, row_ids):
        new_constraint = make_constraint(lp_variables, row, rhs[id],
                                         column_ids, enq_type[id])
        prob.add_constr(new_constraint, name=str(id))
    #print(time() - t0)
    # for row_index in sos_constraints:
    #     sos_set = get_sos(var_list, constraint_matrix_np[row_index], column_ids)
    #   prob.add_sos(list(zip(sos_set, [0 for var in sos_set])), 1)

    # 4. Solve the problem
    k = prob.add_var(var_type=BINARY, obj=1.0)
    #tc = 0
    #t0 = time()
    status = prob.optimize()
    #tc += time() - t0
    #print(tc)
    if status != OptimizationStatus.OPTIMAL:
        raise ValueError('Linear program infeasible')

    # 5. Retrieve optimal values of each variable
    #t0 = time()
    decision_variables = decision_variables.droplevel(1)
    decision_variables['lp_variables'] = [
        lp_variables[i] for i in decision_variables['variable_id']
    ]
    decision_variables['value'] = decision_variables['lp_variables'].apply(
        lambda x: x.x)
    decision_variables = decision_variables.drop('lp_variables', axis=1)
    split_decision_variables = {}
    for variable_group in decision_variables.index.unique():
        split_decision_variables[variable_group] = \
            decision_variables[decision_variables.index == variable_group].reset_index(drop=True)
    #print('get values {}'.format(time() - t0))

    # 6. Retrieve the shadow costs of market constraints
    start_obj = prob.objective.x
    #initial_solution = [(v, v.x) for v in list(sos_variables['vars']) if v.x > 0.01]
    #print(initial_solution)
    #prob.start = initial_solution
    #prob.validate_mip_start()
    for constraint_group in market_rhs_and_type.keys():
        cg = constraint_group
        market_rhs_and_type[cg]['price'] = 0.0
        for id in list(market_rhs_and_type[cg]['constraint_id']):
            constraint = prob.constr_by_name(str(id))
            constraint.rhs += 1.0
            #t0 = time()
            prob.optimize()
            #tc += time() - t0
            marginal_cost = prob.objective.x - start_obj
            market_rhs_and_type[cg].loc[
                market_rhs_and_type[cg]['constraint_id'] == id,
                'price'] = marginal_cost
            constraint.rhs -= 1.0
        # market_rhs_and_type[constraint_group]['price'] = \
        #     market_rhs_and_type[constraint_group].apply(lambda x: get_price(x['constraint_id'], prob), axis=1)
    #print(tc)
    return split_decision_variables, market_rhs_and_type