Source code for flopt.solvers.gurobi_search

import gurobipy

from flopt.solvers.base import BaseSearch
from flopt.expression import Const
from flopt.solution import Solution
from flopt.constants import (
    VariableType,
    ExpressionType,
    ConstraintType,
    SolverTerminateState,
)
from flopt.env import setup_logger

logger = setup_logger(__name__)


class GpVar:
    def __init__(self, gp_var):
        self.gp_var = gp_var

    def value(self):
        return self.gp_var

    def getValue(self):
        return self.gp_var.X

    @property
    def name(self):
        return self.gp_var.VarName

    def getName(self):
        return self.name

    @property
    def Vtype(self):
        return self.gp_var.Vtype


[docs]class GurobiSearch(BaseSearch): """API for Gurobi Parameters ---------- gurobi_params : dict key is name, value is parameter value Examples -------- .. code-block:: python import flopt # Variables a = flopt.Variable("a", lowBound=0, upBound=1, cat="Integer") b = flopt.Variable("b", lowBound=1, upBound=2, cat="Continuous") c = flopt.Variable("c", lowBound=-1, upBound=3, cat="Continuous") # Problem prob = flopt.Problem() prob += a + b + c * c + 2 prob += a + b >= 2 prob += b - c >= 3 prob.solve(solver="gurobi", msg=True) To set parameter for Gurobi, you use gurobi_params argument. .. code-block:: python solver = flopt.Solver("gurobi") solver.setParams(gurobi_params={"LogToConsole": 0}) # set LogToConsole to 0 prob.solve(solver=solver) """ name = "Gurobi" can_solve_problems = { "Variable": VariableType.Number, "Objective": ExpressionType.Quadratic, "Constraint": ExpressionType.Quadratic, } def __init__(self): super().__init__() self.gurobi_params = None self.compute_iis = False def search(self, solution, *args): self.start_build() gp_model, gp_solution = self.createGpProblem(solution, self.prob) self.end_build() # set parameters gp_model.setParam("TimeLimit", self.timelimit - self.build_time) if self.gurobi_params is not None: for key, value in self.gurobi_params.items(): gp_model.setParam(key, value) # solve gp_model.optimize() # Status code Value Description # LOADED 1 Model is loaded, but no solution information is available. # OPTIMAL 2 Model was solved to optimality (subject to tolerances), and an optimal solution is available. # INFEASIBLE 3 Model was proven to be infeasible. # INF_OR_UNBD 4 Model was proven to be either infeasible or unbounded. To obtain a more definitive conclusion, set the DualReductions parameter to 0 and reoptimize. # UNBOUNDED 5 Model was proven to be unbounded. Important note: an unbounded status indicates the presence of an unbounded ray that allows the objective to improve without limit. It says nothing about whether the model has a feasible solution. If you require information on feasibility, you should set the objective to zero and reoptimize. # CUTOFF 6 Optimal objective for model was proven to be worse than the value specified in the Cutoff parameter. No solution information is available. # ITERATION_LIMIT 7 Optimization terminated because the total number of simplex iterations performed exceeded the value specified in the IterationLimit parameter, or because the total number of barrier iterations exceeded the value specified in the BarIterLimit parameter. # NODE_LIMIT 8 Optimization terminated because the total number of branch-and-cut nodes explored exceeded the value specified in the NodeLimit parameter. # TIME_LIMIT 9 Optimization terminated because the time expended exceeded the value specified in the TimeLimit parameter. # SOLUTION_LIMIT 10 Optimization terminated because the number of solutions found reached the value specified in the SolutionLimit parameter. # INTERRUPTED 11 Optimization was terminated by the user. # NUMERIC 12 Optimization was terminated due to unrecoverable numerical difficulties. # SUBOPTIMAL 13 Unable to satisfy optimality tolerances; a sub-optimal solution is available. # INPROGRESS 14 An asynchronous optimization call was made, but the associated optimization run is not yet complete. # USER_OBJ_LIMIT 15 User specified an objective limit (a bound on either the best objective or the best bound), and that limit has been reached. # WORK_LIMIT 16 Optimization terminated because the work expended exceeded the value specified in the WorkLimit parameter. # MEM_LIMIT 17 Optimization terminated because the total amount of allocated memory exceeded the value specified in the SoftMemLimit parameter. logger.debug(f"Gurobi Status {gp_model.status}") if gp_model.status == 3: status = SolverTerminateState.Infeasible if self.compute_iis: logger.info("Model is infeasible, then compute IIS ...") gp_model.computeIIS() gp_model.write(f"{self.prob.name}.ilp") logger.info(f"Write IIS file as {self.prob.name}.ilp") return status elif gp_model.status == 5: status = SolverTerminateState.Unbounded return status elif gp_model.status == 9: status = SolverTerminateState.Timelimit elif gp_model.status == 11: status = SolverTerminateState.Interrupted elif gp_model.status == 2: status = SolverTerminateState.Normal else: status = SolverTerminateState.Abnormal # get result for gp_var in gp_solution: name = gp_var.getName() value = gp_var.getValue() if gp_var.Vtype in {gurobipy.GRB.BINARY, gurobipy.GRB.INTEGER}: value = round(value) solution.setValue(name, value) # update best solution if needed self.registerSolution(solution) return status def createGpProblem(self, solution, prob): """Convert Problem into gurobi.Model Parameters ---------- solution : Solution prob : Problem Returns ------- gurobi.Model, Solution """ name = "" if self.name is None else self.name gp_model = gurobipy.Model(name=name) gp_variables = list() for var in solution: var_name = var.getName() if var.type() == VariableType.Binary: vtype = gurobipy.GRB.BINARY elif var.type() == VariableType.Integer: vtype = gurobipy.GRB.INTEGER elif var.type() == VariableType.Continuous: vtype = gurobipy.GRB.CONTINUOUS else: raise ValueError(var.type()) var_lb = var.getLb() if var.getLb() is not None else -float("inf") var_ub = var.getUb() if var.getUb() is not None else float("inf") gp_var = gp_model.addVar(name=var_name, vtype=vtype, lb=var_lb, ub=var_ub) gp_var = GpVar(gp_var) gp_variables.append(gp_var) gp_model.update() gp_solution = Solution(gp_variables) # conver Problem -> pulp.LpProblem gp_obj = prob.obj.value(gp_solution) gp_sense = ( gurobipy.GRB.MINIMIZE if prob.sense in {"minimize", "Minimize"} else gurobipy.GRB.MAXIMIZE ) gp_model.setObjective(gp_obj, gp_sense) for const in prob.getConstraints(): const_exp = const.expression.value(gp_solution) const_name = const.name if const.name is not None else "" if not isinstance(const_exp, (int, float)): if const.type() == ConstraintType.Eq: gp_model.addConstr(const_exp == 0, name=const_name) else: # const.type() == ConstraintType.Le gp_model.addConstr(const_exp <= 0, name=const_name) gp_model.update() return gp_model, gp_solution