Use compact variable features everywhere

This commit is contained in:
2021-04-15 09:49:35 -05:00
parent fec0113722
commit 95e326f5f6
11 changed files with 147 additions and 374 deletions

View File

@@ -6,11 +6,11 @@ import re
import sys
from io import StringIO
from random import randint
from typing import List, Any, Dict, Optional, Hashable, Tuple, cast, TYPE_CHECKING
from typing import List, Any, Dict, Optional, Hashable, Tuple, TYPE_CHECKING
from overrides import overrides
from miplearn.features import Constraint, Variable, VariableFeatures
from miplearn.features import Constraint, VariableFeatures
from miplearn.instance.base import Instance
from miplearn.solvers import _RedirectOutput
from miplearn.solvers.internal import (
@@ -289,89 +289,6 @@ class GurobiSolver(InternalSolver):
"values",
]
@overrides
def get_variables_old(
self,
with_static: bool = True,
with_sa: bool = True,
) -> Dict[str, Variable]:
assert self.model is not None
names = self._var_names
ub = self._var_ubs
lb = self._var_lbs
obj_coeff = self._var_obj_coeffs
values = None
rc = None
sa_obj_up = None
sa_obj_down = None
sa_ub_up = None
sa_ub_down = None
sa_lb_up = None
sa_lb_down = None
vbasis = None
if self.model.solCount > 0:
values = self.model.getAttr("x", self._gp_vars)
if self._has_lp_solution:
rc = self.model.getAttr("rc", self._gp_vars)
vbasis = self.model.getAttr("vbasis", self._gp_vars)
if with_sa:
sa_obj_up = self.model.getAttr("saobjUp", self._gp_vars)
sa_obj_down = self.model.getAttr("saobjLow", self._gp_vars)
sa_ub_up = self.model.getAttr("saubUp", self._gp_vars)
sa_ub_down = self.model.getAttr("saubLow", self._gp_vars)
sa_lb_up = self.model.getAttr("salbUp", self._gp_vars)
sa_lb_down = self.model.getAttr("salbLow", self._gp_vars)
variables = {}
for (i, gp_var) in enumerate(self._gp_vars):
assert len(names[i]) > 0, "Empty variable name detected."
assert (
names[i] not in variables
), f"Duplicated variable name detected: {names[i]}"
var = Variable()
if with_static:
assert lb is not None
assert ub is not None
assert obj_coeff is not None
var.lower_bound = lb[i]
var.upper_bound = ub[i]
var.obj_coeff = obj_coeff[i]
var.type = self._var_types[i]
if values is not None:
var.value = values[i]
if rc is not None:
assert vbasis is not None
var.reduced_cost = rc[i]
if vbasis[i] == 0:
var.basis_status = "B"
elif vbasis[i] == -1:
var.basis_status = "L"
elif vbasis[i] == -2:
var.basis_status = "U"
elif vbasis[i] == -3:
var.basis_status = "S"
else:
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
return variables
@overrides
def get_variables(
self,
@@ -651,27 +568,6 @@ class GurobiSolver(InternalSolver):
"get_value cannot be called from cb_where=%s" % self.cb_where
)
@staticmethod
def _parse_gurobi_var_lp(gp_var: Any, var: Variable) -> None:
var.reduced_cost = gp_var.rc
var.sa_obj_up = gp_var.saobjUp
var.sa_obj_down = gp_var.saobjLow
var.sa_ub_up = gp_var.saubUp
var.sa_ub_down = gp_var.saubLow
var.sa_lb_up = gp_var.salbUp
var.sa_lb_down = gp_var.salbLow
vbasis = gp_var.vbasis
if vbasis == 0:
var.basis_status = "B"
elif vbasis == -1:
var.basis_status = "L"
elif vbasis == -2:
var.basis_status = "U"
elif vbasis == -3:
var.basis_status = "S"
else:
raise Exception(f"unknown vbasis: {vbasis}")
def _raise_if_callback(self) -> None:
if self.cb_where is not None:
raise Exception("method cannot be called from a callback")

View File

@@ -9,7 +9,7 @@ from typing import Any, Dict, List, Optional
from overrides import EnforceOverrides
from miplearn.features import Constraint, Variable, VariableFeatures
from miplearn.features import Constraint, VariableFeatures
from miplearn.instance.base import Instance
from miplearn.types import (
IterationCallback,
@@ -17,7 +17,6 @@ from miplearn.types import (
BranchPriorities,
UserCutCallback,
Solution,
VariableName,
)
logger = logging.getLogger(__name__)
@@ -236,10 +235,6 @@ class InternalSolver(ABC, EnforceOverrides):
"""
return False
@abstractmethod
def get_variables_old(self, with_static: bool = True) -> Dict[str, Variable]:
pass
@abstractmethod
def get_variables(
self,

View File

@@ -19,7 +19,7 @@ from pyomo.core.expr.numeric_expr import SumExpression, MonomialTermExpression
from pyomo.opt import TerminationCondition
from pyomo.opt.base.solvers import SolverFactory
from miplearn.features import Variable, VariableFeatures
from miplearn.features import VariableFeatures
from miplearn.instance.base import Instance
from miplearn.solvers import _RedirectOutput
from miplearn.solvers.internal import (
@@ -175,21 +175,6 @@ class BasePyomoSolver(InternalSolver):
solution[f"{var}[{index}]"] = var[index].value
return solution
@overrides
def get_variables_old(self, with_static: bool = True) -> Dict[str, Variable]:
assert self.model is not None
variables = {}
for var in self.model.component_objects(pyomo.core.Var):
for idx in var:
varname = f"{var}[{idx}]"
if idx is None:
varname = str(var)
variables[varname] = self._parse_pyomo_variable(
var[idx],
with_static=with_static,
)
return variables
@overrides
def get_variables(
self,
@@ -495,49 +480,6 @@ class BasePyomoSolver(InternalSolver):
def _get_warm_start_regexp(self) -> Optional[str]:
return None
def _parse_pyomo_variable(
self,
pyomo_var: pyomo.core.Var,
with_static: bool = True,
) -> Variable:
assert self.model is not None
variable = Variable()
if with_static:
# Variable type
vtype: Optional[str] = None
if pyomo_var.domain == pyomo.core.Binary:
vtype = "B"
elif pyomo_var.domain in [
pyomo.core.Reals,
pyomo.core.NonNegativeReals,
pyomo.core.NonPositiveReals,
pyomo.core.NegativeReals,
pyomo.core.PositiveReals,
]:
vtype = "C"
if vtype is None:
raise Exception(f"unknown variable domain: {pyomo_var.domain}")
variable.type = vtype
# Bounds
lb, ub = pyomo_var.bounds
variable.upper_bound = float(ub)
variable.lower_bound = float(lb)
# Objective coefficient
obj_coeff = 0.0
if pyomo_var.name in self._obj:
obj_coeff = self._obj[pyomo_var.name]
variable.obj_coeff = obj_coeff
# Reduced costs
if pyomo_var in self.model.rc:
variable.reduced_cost = self.model.rc[pyomo_var]
variable.value = pyomo_var.value
return variable
def _parse_pyomo_constraint(
self,
pyomo_constr: pyomo.core.Constraint,

View File

@@ -3,14 +3,12 @@
# Released under the modified BSD license. See COPYING.md for more details.
import logging
from typing import Optional, List, Dict
from typing import Optional
from overrides import overrides
from pyomo import environ as pe
from scipy.stats import randint
from miplearn.features import Variable
from miplearn.solvers.gurobi import GurobiSolver
from miplearn.solvers.pyomo.base import BasePyomoSolver
from miplearn.types import SolverParams, BranchPriorities

View File

@@ -3,9 +3,8 @@
# Released under the modified BSD license. See COPYING.md for more details.
from typing import Any, Dict, List
import numpy as np
from miplearn.features import Constraint, Variable, VariableFeatures
from miplearn.features import Constraint, VariableFeatures
from miplearn.solvers.internal import InternalSolver
inf = float("inf")
@@ -22,33 +21,15 @@ def _round_constraints(constraints: Dict[str, Constraint]) -> Dict[str, Constrai
return constraints
def _round_variables(vars: Dict[str, Variable]) -> Dict[str, Variable]:
for (cname, c) in vars.items():
for attr in [
"upper_bound",
"lower_bound",
"obj_coeff",
"value",
"reduced_cost",
"sa_obj_up",
"sa_obj_down",
"sa_ub_up",
"sa_ub_down",
"sa_lb_up",
"sa_lb_down",
]:
if getattr(c, attr) is not None:
setattr(c, attr, round(getattr(c, attr), 6))
if c.alvarez_2017 is not None:
c.alvarez_2017 = list(np.round(c.alvarez_2017, 6))
return vars
def _round(obj: Any) -> Any:
if obj is None:
return None
if isinstance(obj, float):
return round(obj, 6)
if isinstance(obj, tuple):
if obj is None:
return None
return tuple([round(v, 6) for v in obj])
return tuple([_round(v) for v in obj])
if isinstance(obj, list):
return [_round(v) for v in obj]
if isinstance(obj, VariableFeatures):
obj.reduced_costs = _round(obj.reduced_costs)
obj.sa_obj_up = _round(obj.sa_obj_up)
@@ -58,6 +39,7 @@ def _round(obj: Any) -> Any:
obj.sa_ub_up = _round(obj.sa_ub_up)
obj.sa_ub_down = _round(obj.sa_ub_down)
obj.values = _round(obj.values)
obj.alvarez_2017 = _round(obj.alvarez_2017)
return obj