mirror of
https://github.com/ANL-CEEESA/MIPLearn.git
synced 2025-12-06 09:28:51 -06:00
Only include static features in after-load
This commit is contained in:
@@ -62,9 +62,9 @@ class DynamicConstraintsComponent(Component):
|
|||||||
|
|
||||||
# Features
|
# Features
|
||||||
features = []
|
features = []
|
||||||
assert sample.after_lp is not None
|
assert sample.after_load is not None
|
||||||
assert sample.after_lp.instance is not None
|
assert sample.after_load.instance is not None
|
||||||
features.extend(sample.after_lp.instance.to_list())
|
features.extend(sample.after_load.instance.to_list())
|
||||||
features.extend(instance.get_constraint_features(cid))
|
features.extend(instance.get_constraint_features(cid))
|
||||||
for ci in features:
|
for ci in features:
|
||||||
assert isinstance(ci, float), (
|
assert isinstance(ci, float), (
|
||||||
|
|||||||
@@ -149,6 +149,7 @@ class PrimalSolutionComponent(Component):
|
|||||||
x: Dict = {}
|
x: Dict = {}
|
||||||
y: Dict = {}
|
y: Dict = {}
|
||||||
assert sample.after_load is not None
|
assert sample.after_load is not None
|
||||||
|
assert sample.after_load.instance is not None
|
||||||
assert sample.after_load.variables is not None
|
assert sample.after_load.variables is not None
|
||||||
for (var_name, var) in sample.after_load.variables.items():
|
for (var_name, var) in sample.after_load.variables.items():
|
||||||
# Initialize categories
|
# Initialize categories
|
||||||
@@ -160,14 +161,11 @@ class PrimalSolutionComponent(Component):
|
|||||||
y[category] = []
|
y[category] = []
|
||||||
|
|
||||||
# Features
|
# Features
|
||||||
sf = sample.after_load
|
features = list(sample.after_load.instance.to_list())
|
||||||
|
features.extend(sample.after_load.variables[var_name].to_list())
|
||||||
if sample.after_lp is not None:
|
if sample.after_lp is not None:
|
||||||
sf = sample.after_lp
|
assert sample.after_lp.variables is not None
|
||||||
assert sf.instance is not None
|
features.extend(sample.after_lp.variables[var_name].to_list())
|
||||||
features = list(sf.instance.to_list())
|
|
||||||
assert sf.variables is not None
|
|
||||||
assert sf.variables[var_name] is not None
|
|
||||||
features.extend(sf.variables[var_name].to_list())
|
|
||||||
x[category].append(features)
|
x[category].append(features)
|
||||||
|
|
||||||
# Labels
|
# Labels
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ class Constraint:
|
|||||||
category: Optional[Hashable] = None
|
category: Optional[Hashable] = None
|
||||||
dual_value: Optional[float] = None
|
dual_value: Optional[float] = None
|
||||||
lazy: bool = False
|
lazy: bool = False
|
||||||
lhs: Dict[str, float] = lambda: {} # type: ignore
|
lhs: Optional[Dict[str, float]] = None
|
||||||
rhs: float = 0.0
|
rhs: float = 0.0
|
||||||
sa_rhs_down: Optional[float] = None
|
sa_rhs_down: Optional[float] = None
|
||||||
sa_rhs_up: Optional[float] = None
|
sa_rhs_up: Optional[float] = None
|
||||||
@@ -136,14 +136,23 @@ class FeaturesExtractor:
|
|||||||
) -> None:
|
) -> None:
|
||||||
self.solver = internal_solver
|
self.solver = internal_solver
|
||||||
|
|
||||||
def extract(self, instance: "Instance") -> Features:
|
def extract(
|
||||||
|
self,
|
||||||
|
instance: "Instance",
|
||||||
|
with_static: bool = True,
|
||||||
|
) -> Features:
|
||||||
features = Features()
|
features = Features()
|
||||||
features.variables = self.solver.get_variables()
|
features.variables = self.solver.get_variables(
|
||||||
features.constraints = self.solver.get_constraints()
|
with_static=with_static,
|
||||||
self._extract_user_features_vars(instance, features)
|
)
|
||||||
self._extract_user_features_constrs(instance, features)
|
features.constraints = self.solver.get_constraints(
|
||||||
self._extract_user_features_instance(instance, features)
|
with_static=with_static,
|
||||||
self._extract_alvarez_2017(features)
|
)
|
||||||
|
if with_static:
|
||||||
|
self._extract_user_features_vars(instance, features)
|
||||||
|
self._extract_user_features_constrs(instance, features)
|
||||||
|
self._extract_user_features_instance(instance, features)
|
||||||
|
self._extract_alvarez_2017(features)
|
||||||
return features
|
return features
|
||||||
|
|
||||||
def _extract_user_features_vars(
|
def _extract_user_features_vars(
|
||||||
|
|||||||
@@ -84,6 +84,7 @@ 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 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
|
||||||
for (varname, coeff) in constr.lhs.items()
|
for (varname, coeff) in constr.lhs.items()
|
||||||
@@ -153,23 +154,36 @@ class GurobiSolver(InternalSolver):
|
|||||||
]
|
]
|
||||||
|
|
||||||
@overrides
|
@overrides
|
||||||
def get_constraints(self) -> Dict[str, Constraint]:
|
def get_constraints(self, with_static: bool = True) -> Dict[str, Constraint]:
|
||||||
model = self.model
|
model = self.model
|
||||||
assert model is not None
|
assert model is not None
|
||||||
self._raise_if_callback()
|
self._raise_if_callback()
|
||||||
if self._dirty:
|
if self._dirty:
|
||||||
model.update()
|
model.update()
|
||||||
self._dirty = False
|
self._dirty = False
|
||||||
|
|
||||||
gp_constrs = model.getConstrs()
|
gp_constrs = model.getConstrs()
|
||||||
var_names = model.getAttr("varName", model.getVars())
|
|
||||||
constr_names = model.getAttr("constrName", gp_constrs)
|
constr_names = model.getAttr("constrName", gp_constrs)
|
||||||
rhs = model.getAttr("rhs", gp_constrs)
|
lhs: Optional[List[Dict]] = None
|
||||||
sense = model.getAttr("sense", gp_constrs)
|
rhs = None
|
||||||
|
sense = None
|
||||||
dual_value = None
|
dual_value = None
|
||||||
sa_rhs_up = None
|
sa_rhs_up = None
|
||||||
sa_rhs_down = None
|
sa_rhs_down = None
|
||||||
slack = None
|
slack = None
|
||||||
basis_status = None
|
basis_status = None
|
||||||
|
|
||||||
|
if with_static:
|
||||||
|
var_names = model.getAttr("varName", model.getVars())
|
||||||
|
rhs = model.getAttr("rhs", gp_constrs)
|
||||||
|
sense = model.getAttr("sense", gp_constrs)
|
||||||
|
lhs = []
|
||||||
|
for (i, gp_constr) in enumerate(gp_constrs):
|
||||||
|
expr = model.getRow(gp_constr)
|
||||||
|
lhsi = {}
|
||||||
|
for j in range(expr.size()):
|
||||||
|
lhsi[var_names[expr.getVar(j).index]] = expr.getCoeff(j)
|
||||||
|
lhs.append(lhsi)
|
||||||
if self._has_lp_solution:
|
if self._has_lp_solution:
|
||||||
dual_value = model.getAttr("pi", gp_constrs)
|
dual_value = model.getAttr("pi", gp_constrs)
|
||||||
sa_rhs_up = model.getAttr("saRhsUp", gp_constrs)
|
sa_rhs_up = model.getAttr("saRhsUp", gp_constrs)
|
||||||
@@ -177,16 +191,20 @@ class GurobiSolver(InternalSolver):
|
|||||||
basis_status = model.getAttr("cbasis", gp_constrs)
|
basis_status = model.getAttr("cbasis", gp_constrs)
|
||||||
if self._has_lp_solution or self._has_mip_solution:
|
if self._has_lp_solution or self._has_mip_solution:
|
||||||
slack = model.getAttr("slack", gp_constrs)
|
slack = model.getAttr("slack", gp_constrs)
|
||||||
|
|
||||||
constraints: Dict[str, Constraint] = {}
|
constraints: Dict[str, Constraint] = {}
|
||||||
for (i, gp_constr) in enumerate(gp_constrs):
|
for (i, gp_constr) in enumerate(gp_constrs):
|
||||||
expr = model.getRow(gp_constr)
|
|
||||||
lhs = {}
|
|
||||||
for j in range(expr.size()):
|
|
||||||
lhs[var_names[expr.getVar(j).index]] = expr.getCoeff(j)
|
|
||||||
assert (
|
assert (
|
||||||
constr_names[i] not in constraints
|
constr_names[i] not in constraints
|
||||||
), f"Duplicated constraint name detected: {constr_names[i]}"
|
), f"Duplicated constraint name detected: {constr_names[i]}"
|
||||||
constraint = Constraint(lhs=lhs, rhs=rhs[i], sense=sense[i])
|
constraint = Constraint()
|
||||||
|
if with_static:
|
||||||
|
assert lhs is not None
|
||||||
|
assert rhs is not None
|
||||||
|
assert sense is not None
|
||||||
|
constraint.lhs = lhs[i]
|
||||||
|
constraint.rhs = rhs[i]
|
||||||
|
constraint.sense = sense[i]
|
||||||
if dual_value is not None:
|
if dual_value is not None:
|
||||||
assert sa_rhs_up is not None
|
assert sa_rhs_up is not None
|
||||||
assert sa_rhs_down is not None
|
assert sa_rhs_down is not None
|
||||||
@@ -247,13 +265,10 @@ class GurobiSolver(InternalSolver):
|
|||||||
]
|
]
|
||||||
|
|
||||||
@overrides
|
@overrides
|
||||||
def get_variables(self) -> Dict[str, Variable]:
|
def get_variables(self, with_static: bool = True) -> Dict[str, Variable]:
|
||||||
assert self.model is not None
|
assert self.model is not None
|
||||||
variables = {}
|
variables = {}
|
||||||
gp_vars = self.model.getVars()
|
gp_vars = self.model.getVars()
|
||||||
lb = self.model.getAttr("lb", gp_vars)
|
|
||||||
ub = self.model.getAttr("ub", gp_vars)
|
|
||||||
obj_coeff = self.model.getAttr("obj", gp_vars)
|
|
||||||
names = self.model.getAttr("varName", gp_vars)
|
names = self.model.getAttr("varName", gp_vars)
|
||||||
values = None
|
values = None
|
||||||
rc = None
|
rc = None
|
||||||
@@ -264,6 +279,13 @@ 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", gp_vars)
|
||||||
if self._has_lp_solution:
|
if self._has_lp_solution:
|
||||||
@@ -281,12 +303,15 @@ class GurobiSolver(InternalSolver):
|
|||||||
assert (
|
assert (
|
||||||
names[i] not in variables
|
names[i] not in variables
|
||||||
), f"Duplicated variable name detected: {names[i]}"
|
), f"Duplicated variable name detected: {names[i]}"
|
||||||
var = Variable(
|
var = Variable()
|
||||||
lower_bound=lb[i],
|
if with_static:
|
||||||
upper_bound=ub[i],
|
assert lb is not None
|
||||||
obj_coeff=obj_coeff[i],
|
assert ub is not None
|
||||||
type=self._original_vtype[gp_var],
|
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._original_vtype[gp_var]
|
||||||
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:
|
||||||
@@ -319,6 +344,7 @@ class GurobiSolver(InternalSolver):
|
|||||||
|
|
||||||
@overrides
|
@overrides
|
||||||
def is_constraint_satisfied(self, constr: Constraint, tol: float = 1e-6) -> bool:
|
def is_constraint_satisfied(self, constr: Constraint, tol: float = 1e-6) -> bool:
|
||||||
|
assert constr.lhs is not None
|
||||||
lhs = 0.0
|
lhs = 0.0
|
||||||
for (varname, coeff) in constr.lhs.items():
|
for (varname, coeff) in constr.lhs.items():
|
||||||
var = self._varname_to_var[varname]
|
var = self._varname_to_var[varname]
|
||||||
|
|||||||
@@ -170,7 +170,7 @@ class InternalSolver(ABC, EnforceOverrides):
|
|||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def get_constraints(self) -> Dict[str, Constraint]:
|
def get_constraints(self, with_static: bool = True) -> Dict[str, Constraint]:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
@@ -237,7 +237,7 @@ class InternalSolver(ABC, EnforceOverrides):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def get_variables(self) -> Dict[str, Variable]:
|
def get_variables(self, with_static: bool = True) -> Dict[str, Variable]:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
|
|||||||
@@ -187,7 +187,10 @@ class LearningSolver:
|
|||||||
# Extract features (after-lp)
|
# Extract features (after-lp)
|
||||||
# -------------------------------------------------------
|
# -------------------------------------------------------
|
||||||
logger.info("Extracting features (after-lp)...")
|
logger.info("Extracting features (after-lp)...")
|
||||||
features = FeaturesExtractor(self.internal_solver).extract(instance)
|
features = FeaturesExtractor(self.internal_solver).extract(
|
||||||
|
instance,
|
||||||
|
with_static=False,
|
||||||
|
)
|
||||||
features.extra = {}
|
features.extra = {}
|
||||||
features.lp_solve = lp_stats
|
features.lp_solve = lp_stats
|
||||||
sample.after_lp = features
|
sample.after_lp = features
|
||||||
@@ -249,7 +252,10 @@ class LearningSolver:
|
|||||||
# Extract features (after-mip)
|
# Extract features (after-mip)
|
||||||
# -------------------------------------------------------
|
# -------------------------------------------------------
|
||||||
logger.info("Extracting features (after-mip)...")
|
logger.info("Extracting features (after-mip)...")
|
||||||
features = FeaturesExtractor(self.internal_solver).extract(instance)
|
features = FeaturesExtractor(self.internal_solver).extract(
|
||||||
|
instance,
|
||||||
|
with_static=False,
|
||||||
|
)
|
||||||
features.mip_solve = mip_stats
|
features.mip_solve = mip_stats
|
||||||
features.extra = {}
|
features.extra = {}
|
||||||
sample.after_mip = features
|
sample.after_mip = features
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ class BasePyomoSolver(InternalSolver):
|
|||||||
) -> None:
|
) -> None:
|
||||||
assert self.model is not None
|
assert self.model is not None
|
||||||
if isinstance(constr, Constraint):
|
if isinstance(constr, Constraint):
|
||||||
|
assert constr.lhs is not None
|
||||||
lhs = 0.0
|
lhs = 0.0
|
||||||
for (varname, coeff) in constr.lhs.items():
|
for (varname, coeff) in constr.lhs.items():
|
||||||
var = self._varname_to_var[varname]
|
var = self._varname_to_var[varname]
|
||||||
@@ -127,7 +128,7 @@ class BasePyomoSolver(InternalSolver):
|
|||||||
self._pyomo_solver.update_var(var)
|
self._pyomo_solver.update_var(var)
|
||||||
|
|
||||||
@overrides
|
@overrides
|
||||||
def get_constraints(self) -> Dict[str, Constraint]:
|
def get_constraints(self, with_static: bool = True) -> Dict[str, Constraint]:
|
||||||
assert self.model is not None
|
assert self.model is not None
|
||||||
|
|
||||||
constraints = {}
|
constraints = {}
|
||||||
@@ -136,11 +137,17 @@ class BasePyomoSolver(InternalSolver):
|
|||||||
for idx in constr:
|
for idx in constr:
|
||||||
name = f"{constr.name}[{idx}]"
|
name = f"{constr.name}[{idx}]"
|
||||||
assert name not in constraints
|
assert name not in constraints
|
||||||
constraints[name] = self._parse_pyomo_constraint(constr[idx])
|
constraints[name] = self._parse_pyomo_constraint(
|
||||||
|
constr[idx],
|
||||||
|
with_static=with_static,
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
name = constr.name
|
name = constr.name
|
||||||
assert name not in constraints
|
assert name not in constraints
|
||||||
constraints[name] = self._parse_pyomo_constraint(constr)
|
constraints[name] = self._parse_pyomo_constraint(
|
||||||
|
constr,
|
||||||
|
with_static=with_static,
|
||||||
|
)
|
||||||
|
|
||||||
return constraints
|
return constraints
|
||||||
|
|
||||||
@@ -169,7 +176,7 @@ class BasePyomoSolver(InternalSolver):
|
|||||||
return solution
|
return solution
|
||||||
|
|
||||||
@overrides
|
@overrides
|
||||||
def get_variables(self) -> Dict[str, Variable]:
|
def get_variables(self, with_static: bool = True) -> Dict[str, Variable]:
|
||||||
assert self.model is not None
|
assert self.model is not None
|
||||||
variables = {}
|
variables = {}
|
||||||
for var in self.model.component_objects(pyomo.core.Var):
|
for var in self.model.component_objects(pyomo.core.Var):
|
||||||
@@ -177,7 +184,10 @@ class BasePyomoSolver(InternalSolver):
|
|||||||
varname = f"{var}[{idx}]"
|
varname = f"{var}[{idx}]"
|
||||||
if idx is None:
|
if idx is None:
|
||||||
varname = str(var)
|
varname = str(var)
|
||||||
variables[varname] = self._parse_pyomo_variable(var[idx])
|
variables[varname] = self._parse_pyomo_variable(
|
||||||
|
var[idx],
|
||||||
|
with_static=with_static,
|
||||||
|
)
|
||||||
return variables
|
return variables
|
||||||
|
|
||||||
@overrides
|
@overrides
|
||||||
@@ -201,6 +211,7 @@ class BasePyomoSolver(InternalSolver):
|
|||||||
@overrides
|
@overrides
|
||||||
def is_constraint_satisfied(self, constr: Constraint, tol: float = 1e-6) -> bool:
|
def is_constraint_satisfied(self, constr: Constraint, tol: float = 1e-6) -> bool:
|
||||||
lhs = 0.0
|
lhs = 0.0
|
||||||
|
assert constr.lhs is not None
|
||||||
for (varname, coeff) in constr.lhs.items():
|
for (varname, coeff) in constr.lhs.items():
|
||||||
var = self._varname_to_var[varname]
|
var = self._varname_to_var[varname]
|
||||||
lhs += var.value * coeff
|
lhs += var.value * coeff
|
||||||
@@ -378,71 +389,78 @@ class BasePyomoSolver(InternalSolver):
|
|||||||
def _get_warm_start_regexp(self) -> Optional[str]:
|
def _get_warm_start_regexp(self) -> Optional[str]:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _parse_pyomo_variable(self, var: pyomo.core.Var) -> Variable:
|
def _parse_pyomo_variable(
|
||||||
|
self,
|
||||||
|
pyomo_var: pyomo.core.Var,
|
||||||
|
with_static: bool = True,
|
||||||
|
) -> Variable:
|
||||||
assert self.model is not None
|
assert self.model is not None
|
||||||
|
variable = Variable()
|
||||||
|
|
||||||
# Variable type
|
if with_static:
|
||||||
vtype: Optional[str] = None
|
# Variable type
|
||||||
if var.domain == pyomo.core.Binary:
|
vtype: Optional[str] = None
|
||||||
vtype = "B"
|
if pyomo_var.domain == pyomo.core.Binary:
|
||||||
elif var.domain in [
|
vtype = "B"
|
||||||
pyomo.core.Reals,
|
elif pyomo_var.domain in [
|
||||||
pyomo.core.NonNegativeReals,
|
pyomo.core.Reals,
|
||||||
pyomo.core.NonPositiveReals,
|
pyomo.core.NonNegativeReals,
|
||||||
pyomo.core.NegativeReals,
|
pyomo.core.NonPositiveReals,
|
||||||
pyomo.core.PositiveReals,
|
pyomo.core.NegativeReals,
|
||||||
]:
|
pyomo.core.PositiveReals,
|
||||||
vtype = "C"
|
]:
|
||||||
if vtype is None:
|
vtype = "C"
|
||||||
raise Exception(f"unknown variable domain: {var.domain}")
|
if vtype is None:
|
||||||
|
raise Exception(f"unknown variable domain: {pyomo_var.domain}")
|
||||||
|
variable.type = vtype
|
||||||
|
|
||||||
# Bounds
|
# Bounds
|
||||||
lb, ub = var.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
|
# Reduced costs
|
||||||
rc = None
|
if pyomo_var in self.model.rc:
|
||||||
if var in self.model.rc:
|
variable.reduced_cost = self.model.rc[pyomo_var]
|
||||||
rc = self.model.rc[var]
|
|
||||||
|
|
||||||
# Objective coefficient
|
variable.value = pyomo_var.value
|
||||||
obj_coeff = 0.0
|
return variable
|
||||||
if var.name in self._obj:
|
|
||||||
obj_coeff = self._obj[var.name]
|
|
||||||
|
|
||||||
return Variable(
|
|
||||||
value=var.value,
|
|
||||||
type=vtype,
|
|
||||||
lower_bound=float(lb),
|
|
||||||
upper_bound=float(ub),
|
|
||||||
obj_coeff=obj_coeff,
|
|
||||||
reduced_cost=rc,
|
|
||||||
)
|
|
||||||
|
|
||||||
def _parse_pyomo_constraint(
|
def _parse_pyomo_constraint(
|
||||||
self,
|
self,
|
||||||
pyomo_constr: pyomo.core.Constraint,
|
pyomo_constr: pyomo.core.Constraint,
|
||||||
|
with_static: bool = True,
|
||||||
) -> Constraint:
|
) -> Constraint:
|
||||||
assert self.model is not None
|
assert self.model is not None
|
||||||
constr = Constraint()
|
constr = Constraint()
|
||||||
|
|
||||||
# Extract RHS and sense
|
if with_static:
|
||||||
has_ub = pyomo_constr.has_ub()
|
# Extract RHS and sense
|
||||||
has_lb = pyomo_constr.has_lb()
|
has_ub = pyomo_constr.has_ub()
|
||||||
assert (
|
has_lb = pyomo_constr.has_lb()
|
||||||
(not has_lb) or (not has_ub) or pyomo_constr.upper() == pyomo_constr.lower()
|
assert (
|
||||||
), "range constraints not supported"
|
(not has_lb)
|
||||||
if not has_ub:
|
or (not has_ub)
|
||||||
constr.sense = ">"
|
or pyomo_constr.upper() == pyomo_constr.lower()
|
||||||
constr.rhs = pyomo_constr.lower()
|
), "range constraints not supported"
|
||||||
elif not has_lb:
|
if not has_ub:
|
||||||
constr.sense = "<"
|
constr.sense = ">"
|
||||||
constr.rhs = pyomo_constr.upper()
|
constr.rhs = pyomo_constr.lower()
|
||||||
else:
|
elif not has_lb:
|
||||||
constr.sense = "="
|
constr.sense = "<"
|
||||||
constr.rhs = pyomo_constr.upper()
|
constr.rhs = pyomo_constr.upper()
|
||||||
|
else:
|
||||||
|
constr.sense = "="
|
||||||
|
constr.rhs = pyomo_constr.upper()
|
||||||
|
|
||||||
# Extract LHS
|
# Extract LHS
|
||||||
constr.lhs = self._parse_pyomo_expr(pyomo_constr.body)
|
constr.lhs = self._parse_pyomo_expr(pyomo_constr.body)
|
||||||
|
|
||||||
# Extract solution attributes
|
# Extract solution attributes
|
||||||
if self._has_lp_solution:
|
if self._has_lp_solution:
|
||||||
|
|||||||
@@ -55,37 +55,6 @@ class GurobiPyomoSolver(BasePyomoSolver):
|
|||||||
gvar = self._pyomo_solver._pyomo_var_to_solver_var_map[var]
|
gvar = self._pyomo_solver._pyomo_var_to_solver_var_map[var]
|
||||||
gvar.setAttr(GRB.Attr.BranchPriority, int(round(priority)))
|
gvar.setAttr(GRB.Attr.BranchPriority, int(round(priority)))
|
||||||
|
|
||||||
@overrides
|
|
||||||
def get_variables(self) -> Dict[str, Variable]:
|
|
||||||
variables = super().get_variables()
|
|
||||||
if self._has_lp_solution:
|
|
||||||
for (varname, var) in variables.items():
|
|
||||||
pvar = self._varname_to_var[varname]
|
|
||||||
gvar = self._pyomo_solver._pyomo_var_to_solver_var_map[pvar]
|
|
||||||
GurobiSolver._parse_gurobi_var_lp(gvar, var)
|
|
||||||
|
|
||||||
return variables
|
|
||||||
|
|
||||||
@overrides
|
|
||||||
def get_variable_attrs(self) -> List[str]:
|
|
||||||
return [
|
|
||||||
"basis_status",
|
|
||||||
"category",
|
|
||||||
"lower_bound",
|
|
||||||
"obj_coeff",
|
|
||||||
"reduced_cost",
|
|
||||||
"sa_lb_down",
|
|
||||||
"sa_lb_up",
|
|
||||||
"sa_obj_down",
|
|
||||||
"sa_obj_up",
|
|
||||||
"sa_ub_down",
|
|
||||||
"sa_ub_up",
|
|
||||||
"type",
|
|
||||||
"upper_bound",
|
|
||||||
"user_features",
|
|
||||||
"value",
|
|
||||||
]
|
|
||||||
|
|
||||||
@overrides
|
@overrides
|
||||||
def _extract_node_count(self, log: str) -> int:
|
def _extract_node_count(self, log: str) -> int:
|
||||||
return max(1, int(self._pyomo_solver._solver_model.getAttr("NodeCount")))
|
return max(1, int(self._pyomo_solver._solver_model.getAttr("NodeCount")))
|
||||||
|
|||||||
@@ -279,63 +279,25 @@ def run_basic_usage_tests(solver: InternalSolver) -> None:
|
|||||||
assert isinstance(mip_stats.mip_wallclock_time, float)
|
assert isinstance(mip_stats.mip_wallclock_time, float)
|
||||||
assert mip_stats.mip_wallclock_time > 0
|
assert mip_stats.mip_wallclock_time > 0
|
||||||
|
|
||||||
# Fetch variables (after-load)
|
# Fetch variables (after-mip)
|
||||||
assert_equals(
|
assert_equals(
|
||||||
_round_variables(solver.get_variables()),
|
_round_variables(solver.get_variables(with_static=False)),
|
||||||
_remove_unsupported_var_attrs(
|
_remove_unsupported_var_attrs(
|
||||||
solver,
|
solver,
|
||||||
{
|
{
|
||||||
"x[0]": Variable(
|
"x[0]": Variable(value=1.0),
|
||||||
lower_bound=0.0,
|
"x[1]": Variable(value=0.0),
|
||||||
obj_coeff=505.0,
|
"x[2]": Variable(value=1.0),
|
||||||
type="B",
|
"x[3]": Variable(value=1.0),
|
||||||
upper_bound=1.0,
|
"z": Variable(value=61.0),
|
||||||
value=1.0,
|
|
||||||
),
|
|
||||||
"x[1]": Variable(
|
|
||||||
lower_bound=0.0,
|
|
||||||
obj_coeff=352.0,
|
|
||||||
type="B",
|
|
||||||
upper_bound=1.0,
|
|
||||||
value=0.0,
|
|
||||||
),
|
|
||||||
"x[2]": Variable(
|
|
||||||
lower_bound=0.0,
|
|
||||||
obj_coeff=458.0,
|
|
||||||
type="B",
|
|
||||||
upper_bound=1.0,
|
|
||||||
value=1.0,
|
|
||||||
),
|
|
||||||
"x[3]": Variable(
|
|
||||||
lower_bound=0.0,
|
|
||||||
obj_coeff=220.0,
|
|
||||||
type="B",
|
|
||||||
upper_bound=1.0,
|
|
||||||
value=1.0,
|
|
||||||
),
|
|
||||||
"z": Variable(
|
|
||||||
lower_bound=0.0,
|
|
||||||
obj_coeff=0.0,
|
|
||||||
type="C",
|
|
||||||
upper_bound=67.0,
|
|
||||||
value=61.0,
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
# Fetch constraints (after-mip)
|
# Fetch constraints (after-mip)
|
||||||
assert_equals(
|
assert_equals(
|
||||||
_round_constraints(solver.get_constraints()),
|
_round_constraints(solver.get_constraints(with_static=False)),
|
||||||
{
|
{"eq_capacity": Constraint(slack=0.0)},
|
||||||
"eq_capacity": Constraint(
|
|
||||||
lazy=False,
|
|
||||||
lhs={"x[0]": 23.0, "x[1]": 26.0, "x[2]": 20.0, "x[3]": 18.0, "z": -1.0},
|
|
||||||
rhs=0.0,
|
|
||||||
sense="=",
|
|
||||||
slack=0.0,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Build a new constraint
|
# Build a new constraint
|
||||||
|
|||||||
@@ -28,18 +28,18 @@ def training_instances() -> List[Instance]:
|
|||||||
instances = [cast(Instance, Mock(spec=Instance)) for _ in range(2)]
|
instances = [cast(Instance, Mock(spec=Instance)) for _ in range(2)]
|
||||||
instances[0].samples = [
|
instances[0].samples = [
|
||||||
Sample(
|
Sample(
|
||||||
after_lp=Features(instance=InstanceFeatures()),
|
after_load=Features(instance=InstanceFeatures()),
|
||||||
after_mip=Features(extra={"lazy_enforced": {"c1", "c2"}}),
|
after_mip=Features(extra={"lazy_enforced": {"c1", "c2"}}),
|
||||||
),
|
),
|
||||||
Sample(
|
Sample(
|
||||||
after_lp=Features(instance=InstanceFeatures()),
|
after_load=Features(instance=InstanceFeatures()),
|
||||||
after_mip=Features(extra={"lazy_enforced": {"c2", "c3"}}),
|
after_mip=Features(extra={"lazy_enforced": {"c2", "c3"}}),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
instances[0].samples[0].after_lp.instance.to_list = Mock( # type: ignore
|
instances[0].samples[0].after_load.instance.to_list = Mock( # type: ignore
|
||||||
return_value=[5.0]
|
return_value=[5.0]
|
||||||
)
|
)
|
||||||
instances[0].samples[1].after_lp.instance.to_list = Mock( # type: ignore
|
instances[0].samples[1].after_load.instance.to_list = Mock( # type: ignore
|
||||||
return_value=[5.0]
|
return_value=[5.0]
|
||||||
)
|
)
|
||||||
instances[0].get_constraint_category = Mock( # type: ignore
|
instances[0].get_constraint_category = Mock( # type: ignore
|
||||||
@@ -60,11 +60,11 @@ def training_instances() -> List[Instance]:
|
|||||||
)
|
)
|
||||||
instances[1].samples = [
|
instances[1].samples = [
|
||||||
Sample(
|
Sample(
|
||||||
after_lp=Features(instance=InstanceFeatures()),
|
after_load=Features(instance=InstanceFeatures()),
|
||||||
after_mip=Features(extra={"lazy_enforced": {"c3", "c4"}}),
|
after_mip=Features(extra={"lazy_enforced": {"c3", "c4"}}),
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
instances[1].samples[0].after_lp.instance.to_list = Mock( # type: ignore
|
instances[1].samples[0].after_load.instance.to_list = Mock( # type: ignore
|
||||||
return_value=[8.0]
|
return_value=[8.0]
|
||||||
)
|
)
|
||||||
instances[1].get_constraint_category = Mock( # type: ignore
|
instances[1].get_constraint_category = Mock( # type: ignore
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ from miplearn.solvers.tests import assert_equals
|
|||||||
def sample() -> Sample:
|
def sample() -> Sample:
|
||||||
sample = Sample(
|
sample = Sample(
|
||||||
after_load=Features(
|
after_load=Features(
|
||||||
|
instance=InstanceFeatures(),
|
||||||
variables={
|
variables={
|
||||||
"x[0]": Variable(category="default"),
|
"x[0]": Variable(category="default"),
|
||||||
"x[1]": Variable(category=None),
|
"x[1]": Variable(category=None),
|
||||||
@@ -35,7 +36,6 @@ def sample() -> Sample:
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
after_lp=Features(
|
after_lp=Features(
|
||||||
instance=InstanceFeatures(),
|
|
||||||
variables={
|
variables={
|
||||||
"x[0]": Variable(),
|
"x[0]": Variable(),
|
||||||
"x[1]": Variable(),
|
"x[1]": Variable(),
|
||||||
@@ -52,7 +52,7 @@ def sample() -> Sample:
|
|||||||
}
|
}
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
sample.after_lp.instance.to_list = Mock(return_value=[5.0]) # type: ignore
|
sample.after_load.instance.to_list = Mock(return_value=[5.0]) # type: ignore
|
||||||
sample.after_lp.variables["x[0]"].to_list = Mock( # type: ignore
|
sample.after_lp.variables["x[0]"].to_list = Mock( # type: ignore
|
||||||
return_value=[0.0, 0.0]
|
return_value=[0.0, 0.0]
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user