GurobiSolver: Performance improvements

master
Alinson S. Xavier 5 years ago
parent e1f32b1798
commit e6eca2ee7f
No known key found for this signature in database
GPG Key ID: DCA0DAD4D2F58624

@ -169,7 +169,7 @@ class Component(EnforceOverrides):
""" """
pass pass
def pre_fit(self, pre: List[Any]): def pre_fit(self, pre: List[Any]) -> None:
pass pass
def user_cut_cb( def user_cut_cb(

@ -25,8 +25,6 @@ from miplearn.types import (
SolverParams, SolverParams,
UserCutCallback, UserCutCallback,
Solution, Solution,
VariableName,
Category,
) )
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -66,13 +64,18 @@ class GurobiSolver(InternalSolver):
self.params: SolverParams = params self.params: SolverParams = params
self.cb_where: Optional[int] = None self.cb_where: Optional[int] = None
self.lazy_cb_frequency = lazy_cb_frequency self.lazy_cb_frequency = lazy_cb_frequency
self._bin_vars: List["gurobipy.Var"] = []
self._varname_to_var: Dict[str, "gurobipy.Var"] = {}
self._original_vtype: Dict["gurobipy.Var", str] = {}
self._dirty = True self._dirty = True
self._has_lp_solution = False self._has_lp_solution = False
self._has_mip_solution = False self._has_mip_solution = False
self._varname_to_var: Dict[str, "gurobipy.Var"] = {}
self._gp_vars: List["gurobipy.Var"] = []
self._var_names: List[str] = []
self._var_types: List[str] = []
self._var_lbs: List[float] = []
self._var_ubs: List[float] = []
self._var_obj_coeffs: List[float] = []
if self.lazy_cb_frequency == 1: if self.lazy_cb_frequency == 1:
self.lazy_cb_where = [self.gp.GRB.Callback.MIPSOL] self.lazy_cb_where = [self.gp.GRB.Callback.MIPSOL]
else: else:
@ -84,6 +87,8 @@ class GurobiSolver(InternalSolver):
@overrides @overrides
def add_constraint(self, constr: Constraint, name: str) -> None: def add_constraint(self, constr: Constraint, name: str) -> None:
assert self.model is not None assert self.model is not None
assert self._varname_to_var is not None
assert constr.lhs is not None assert constr.lhs is not None
lhs = self.gp.quicksum( lhs = self.gp.quicksum(
self._varname_to_var[varname] * coeff self._varname_to_var[varname] * coeff
@ -265,11 +270,18 @@ class GurobiSolver(InternalSolver):
] ]
@overrides @overrides
def get_variables(self, with_static: bool = True) -> Dict[str, Variable]: def get_variables(
self,
with_static: bool = True,
with_sa: bool = True,
) -> Dict[str, Variable]:
assert self.model is not None assert self.model is not None
variables = {}
gp_vars = self.model.getVars() names = self._var_names
names = self.model.getAttr("varName", gp_vars) ub = self._var_ubs
lb = self._var_lbs
obj_coeff = self._var_obj_coeffs
values = None values = None
rc = None rc = None
sa_obj_up = None sa_obj_up = None
@ -279,26 +291,23 @@ class GurobiSolver(InternalSolver):
sa_lb_up = None sa_lb_up = None
sa_lb_down = None sa_lb_down = None
vbasis = None vbasis = None
ub = None
lb = None
obj_coeff = None
if with_static:
lb = self.model.getAttr("lb", gp_vars)
ub = self.model.getAttr("ub", gp_vars)
obj_coeff = self.model.getAttr("obj", gp_vars)
if self.model.solCount > 0: if self.model.solCount > 0:
values = self.model.getAttr("x", gp_vars) values = self.model.getAttr("x", self._gp_vars)
if self._has_lp_solution: if self._has_lp_solution:
rc = self.model.getAttr("rc", gp_vars) rc = self.model.getAttr("rc", self._gp_vars)
sa_obj_up = self.model.getAttr("saobjUp", gp_vars) vbasis = self.model.getAttr("vbasis", self._gp_vars)
sa_obj_down = self.model.getAttr("saobjLow", gp_vars) if with_sa:
sa_ub_up = self.model.getAttr("saubUp", gp_vars) sa_obj_up = self.model.getAttr("saobjUp", self._gp_vars)
sa_ub_down = self.model.getAttr("saubLow", gp_vars) sa_obj_down = self.model.getAttr("saobjLow", self._gp_vars)
sa_lb_up = self.model.getAttr("salbUp", gp_vars) sa_ub_up = self.model.getAttr("saubUp", self._gp_vars)
sa_lb_down = self.model.getAttr("salbLow", gp_vars) sa_ub_down = self.model.getAttr("saubLow", self._gp_vars)
vbasis = self.model.getAttr("vbasis", gp_vars) sa_lb_up = self.model.getAttr("salbUp", self._gp_vars)
sa_lb_down = self.model.getAttr("salbLow", self._gp_vars)
for (i, gp_var) in enumerate(gp_vars): variables = {}
for (i, gp_var) in enumerate(self._gp_vars):
assert len(names[i]) > 0, "Empty variable name detected." assert len(names[i]) > 0, "Empty variable name detected."
assert ( assert (
names[i] not in variables names[i] not in variables
@ -311,24 +320,12 @@ class GurobiSolver(InternalSolver):
var.lower_bound = lb[i] var.lower_bound = lb[i]
var.upper_bound = ub[i] var.upper_bound = ub[i]
var.obj_coeff = obj_coeff[i] var.obj_coeff = obj_coeff[i]
var.type = self._original_vtype[gp_var] var.type = self._var_types[i]
if values is not None: if values is not None:
var.value = values[i] var.value = values[i]
if rc is not None: if rc is not None:
assert sa_obj_up is not None
assert sa_obj_down is not None
assert sa_ub_up is not None
assert sa_ub_down is not None
assert sa_lb_up is not None
assert sa_lb_down is not None
assert vbasis is not None assert vbasis is not None
var.reduced_cost = rc[i] var.reduced_cost = rc[i]
var.sa_obj_up = sa_obj_up[i]
var.sa_obj_down = sa_obj_down[i]
var.sa_ub_up = sa_ub_up[i]
var.sa_ub_down = sa_ub_down[i]
var.sa_lb_up = sa_lb_up[i]
var.sa_lb_down = sa_lb_down[i]
if vbasis[i] == 0: if vbasis[i] == 0:
var.basis_status = "B" var.basis_status = "B"
elif vbasis[i] == -1: elif vbasis[i] == -1:
@ -339,6 +336,19 @@ class GurobiSolver(InternalSolver):
var.basis_status = "S" var.basis_status = "S"
else: else:
raise Exception(f"unknown vbasis: {vbasis}") raise Exception(f"unknown vbasis: {vbasis}")
if with_sa:
assert sa_obj_up is not None
assert sa_obj_down is not None
assert sa_ub_up is not None
assert sa_ub_down is not None
assert sa_lb_up is not None
assert sa_lb_down is not None
var.sa_obj_up = sa_obj_up[i]
var.sa_obj_down = sa_obj_down[i]
var.sa_ub_up = sa_ub_up[i]
var.sa_ub_down = sa_ub_down[i]
var.sa_lb_up = sa_lb_up[i]
var.sa_lb_down = sa_lb_down[i]
variables[names[i]] = var variables[names[i]] = var
return variables return variables
@ -479,14 +489,16 @@ class GurobiSolver(InternalSolver):
streams += [sys.stdout] streams += [sys.stdout]
self._apply_params(streams) self._apply_params(streams)
assert self.model is not None assert self.model is not None
for var in self._bin_vars: for (i, var) in enumerate(self._gp_vars):
if self._var_types[i] == "B":
var.vtype = self.gp.GRB.CONTINUOUS var.vtype = self.gp.GRB.CONTINUOUS
var.lb = 0.0 var.lb = 0.0
var.ub = 1.0 var.ub = 1.0
with _RedirectOutput(streams): with _RedirectOutput(streams):
self.model.optimize() self.model.optimize()
self._dirty = False self._dirty = False
for var in self._bin_vars: for (i, var) in enumerate(self._gp_vars):
if self._var_types[i] == "B":
var.vtype = self.gp.GRB.BINARY var.vtype = self.gp.GRB.BINARY
log = streams[0].getvalue() log = streams[0].getvalue()
self._has_lp_solution = self.model.solCount > 0 self._has_lp_solution = self.model.solCount > 0
@ -577,33 +589,40 @@ class GurobiSolver(InternalSolver):
def _update_vars(self) -> None: def _update_vars(self) -> None:
assert self.model is not None assert self.model is not None
self._varname_to_var.clear() gp_vars = self.model.getVars()
self._original_vtype = {} var_names = self.model.getAttr("varName", gp_vars)
self._bin_vars.clear() var_types = self.model.getAttr("vtype", gp_vars)
for var in self.model.getVars(): var_ubs = self.model.getAttr("ub", gp_vars)
assert var.varName not in self._varname_to_var, ( var_lbs = self.model.getAttr("lb", gp_vars)
f"Duplicated variable name detected: {var.varName}. " var_obj_coeffs = self.model.getAttr("obj", gp_vars)
f"Unique variable names are currently required." varname_to_var: Dict = {}
for (i, gp_var) in enumerate(gp_vars):
assert var_names[i] not in varname_to_var, (
f"Duplicated variable name detected: {var_names[i]}. "
f"Unique variable var_names are currently required."
) )
self._varname_to_var[var.varName] = var if var_types[i] == "I":
vtype = var.vtype assert var_ubs[i] == 1.0, (
if vtype == "I":
assert var.ub == 1.0, (
"Only binary and continuous variables are currently supported. " "Only binary and continuous variables are currently supported. "
"Integer variable {var.varName} has upper bound {var.ub}." "Integer variable {var.varName} has upper bound {var.ub}."
) )
assert var.lb == 0.0, ( assert var_lbs[i] == 0.0, (
"Only binary and continuous variables are currently supported. " "Only binary and continuous variables are currently supported. "
"Integer variable {var.varName} has lower bound {var.ub}." "Integer variable {var.varName} has lower bound {var.ub}."
) )
vtype = "B" var_types[i] = "B"
assert vtype in ["B", "C"], ( assert var_types[i] in ["B", "C"], (
"Only binary and continuous variables are currently supported. " "Only binary and continuous variables are currently supported. "
"Variable {var.varName} has type {vtype}." "Variable {var.varName} has type {vtype}."
) )
self._original_vtype[var] = vtype varname_to_var[var_names[i]] = gp_var
if vtype == "B": self._varname_to_var = varname_to_var
self._bin_vars.append(var) self._gp_vars = gp_vars
self._var_names = var_names
self._var_types = var_types
self._var_lbs = var_lbs
self._var_ubs = var_ubs
self._var_obj_coeffs = var_obj_coeffs
def __getstate__(self) -> Dict: def __getstate__(self) -> Dict:
return { return {

Loading…
Cancel
Save