mirror of
https://github.com/ANL-CEEESA/MIPLearn.git
synced 2025-12-06 09:28:51 -06:00
Pyomo: Collect variable bounds, obj_coeff, value, type
This commit is contained in:
@@ -12,7 +12,7 @@ import numpy as np
|
||||
import pyomo
|
||||
from overrides import overrides
|
||||
from pyomo import environ as pe
|
||||
from pyomo.core import Var, Suffix
|
||||
from pyomo.core import Var, Suffix, Objective
|
||||
from pyomo.core.base import _GeneralVarData
|
||||
from pyomo.core.base.constraint import ConstraintList
|
||||
from pyomo.core.expr.numeric_expr import SumExpression, MonomialTermExpression
|
||||
@@ -64,6 +64,7 @@ class BasePyomoSolver(InternalSolver):
|
||||
self._termination_condition: str = ""
|
||||
self._has_lp_solution = False
|
||||
self._has_mip_solution = False
|
||||
self._obj: Dict[str, float] = {}
|
||||
|
||||
for (key, value) in params.items():
|
||||
self._pyomo_solver.options[key] = value
|
||||
@@ -223,6 +224,9 @@ class BasePyomoSolver(InternalSolver):
|
||||
self._all_vars += [var[idx]]
|
||||
if var[idx].domain == pyomo.core.base.set_types.Binary:
|
||||
self._bin_vars += [var[idx]]
|
||||
for obj in self.model.component_objects(Objective):
|
||||
self._obj = self._parse_pyomo_expr(obj.expr)
|
||||
break
|
||||
|
||||
def _update_constrs(self) -> None:
|
||||
assert self.model is not None
|
||||
@@ -371,11 +375,42 @@ class BasePyomoSolver(InternalSolver):
|
||||
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])
|
||||
return variables
|
||||
|
||||
def _parse_pyomo_variable(self, var: pyomo.core.Var) -> Variable:
|
||||
return Variable()
|
||||
# Variable type
|
||||
vtype: Optional[str] = None
|
||||
if var.domain == pyomo.core.Binary:
|
||||
vtype = "B"
|
||||
elif 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: {var.domain}")
|
||||
|
||||
# Bounds
|
||||
lb, ub = var.bounds
|
||||
|
||||
# Objective coefficient
|
||||
obj_coeff = 0.0
|
||||
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,
|
||||
)
|
||||
|
||||
@overrides
|
||||
def get_constraints(self) -> Dict[str, Constraint]:
|
||||
@@ -408,10 +443,10 @@ class BasePyomoSolver(InternalSolver):
|
||||
assert (
|
||||
(not has_lb) or (not has_ub) or pyomo_constr.upper() == pyomo_constr.lower()
|
||||
), "range constraints not supported"
|
||||
if has_lb:
|
||||
if not has_ub:
|
||||
constr.sense = ">"
|
||||
constr.rhs = pyomo_constr.lower()
|
||||
elif has_ub:
|
||||
elif not has_lb:
|
||||
constr.sense = "<"
|
||||
constr.rhs = pyomo_constr.upper()
|
||||
else:
|
||||
@@ -419,22 +454,7 @@ class BasePyomoSolver(InternalSolver):
|
||||
constr.rhs = pyomo_constr.upper()
|
||||
|
||||
# Extract LHS
|
||||
lhs = {}
|
||||
if isinstance(pyomo_constr.body, SumExpression):
|
||||
for term in pyomo_constr.body._args_:
|
||||
if isinstance(term, MonomialTermExpression):
|
||||
lhs[term._args_[1].name] = term._args_[0]
|
||||
elif isinstance(term, _GeneralVarData):
|
||||
lhs[term.name] = 1.0
|
||||
else:
|
||||
raise Exception(f"Unknown term type: {term.__class__.__name__}")
|
||||
elif isinstance(pyomo_constr.body, _GeneralVarData):
|
||||
lhs[pyomo_constr.body.name] = 1.0
|
||||
else:
|
||||
raise Exception(
|
||||
f"Unknown expression type: {pyomo_constr.body.__class__.__name__}"
|
||||
)
|
||||
constr.lhs = lhs
|
||||
constr.lhs = self._parse_pyomo_expr(pyomo_constr.body)
|
||||
|
||||
# Extract solution attributes
|
||||
if self._has_lp_solution:
|
||||
@@ -446,6 +466,22 @@ class BasePyomoSolver(InternalSolver):
|
||||
# Build constraint
|
||||
return constr
|
||||
|
||||
def _parse_pyomo_expr(self, expr):
|
||||
lhs = {}
|
||||
if isinstance(expr, SumExpression):
|
||||
for term in expr._args_:
|
||||
if isinstance(term, MonomialTermExpression):
|
||||
lhs[term._args_[1].name] = float(term._args_[0])
|
||||
elif isinstance(term, _GeneralVarData):
|
||||
lhs[term.name] = 1.0
|
||||
else:
|
||||
raise Exception(f"Unknown term type: {term.__class__.__name__}")
|
||||
elif isinstance(expr, _GeneralVarData):
|
||||
lhs[expr.name] = 1.0
|
||||
else:
|
||||
raise Exception(f"Unknown expression type: {expr.__class__.__name__}")
|
||||
return lhs
|
||||
|
||||
@overrides
|
||||
def are_callbacks_supported(self) -> bool:
|
||||
return False
|
||||
@@ -453,23 +489,20 @@ class BasePyomoSolver(InternalSolver):
|
||||
@overrides
|
||||
def get_constraint_attrs(self) -> List[str]:
|
||||
return [
|
||||
"category",
|
||||
"dual_value",
|
||||
"lazy",
|
||||
"lhs",
|
||||
"rhs",
|
||||
"sense",
|
||||
"slack",
|
||||
"user_features",
|
||||
]
|
||||
|
||||
@overrides
|
||||
def get_variable_attrs(self) -> List[str]:
|
||||
return [
|
||||
# "basis_status",
|
||||
# "category",
|
||||
# "lower_bound",
|
||||
# "obj_coeff",
|
||||
"lower_bound",
|
||||
"obj_coeff",
|
||||
# "reduced_cost",
|
||||
# "sa_lb_down",
|
||||
# "sa_lb_up",
|
||||
@@ -477,10 +510,9 @@ class BasePyomoSolver(InternalSolver):
|
||||
# "sa_obj_up",
|
||||
# "sa_ub_down",
|
||||
# "sa_ub_up",
|
||||
# "type",
|
||||
# "upper_bound",
|
||||
# "user_features",
|
||||
# "value",
|
||||
"type",
|
||||
"upper_bound",
|
||||
"value",
|
||||
]
|
||||
|
||||
|
||||
@@ -529,12 +561,13 @@ class PyomoTestInstanceKnapsack(Instance):
|
||||
model = pe.ConcreteModel()
|
||||
items = range(len(self.weights))
|
||||
model.x = pe.Var(items, domain=pe.Binary)
|
||||
model.z = pe.Var(domain=pe.Reals, bounds=(0, self.capacity))
|
||||
model.OBJ = pe.Objective(
|
||||
expr=sum(model.x[v] * self.prices[v] for v in items),
|
||||
sense=pe.maximize,
|
||||
)
|
||||
model.eq_capacity = pe.Constraint(
|
||||
expr=sum(model.x[v] * self.weights[v] for v in items) <= self.capacity
|
||||
expr=sum(model.x[v] * self.weights[v] for v in items) == model.z
|
||||
)
|
||||
return model
|
||||
|
||||
@@ -552,3 +585,9 @@ class PyomoTestInstanceKnapsack(Instance):
|
||||
self.weights[item],
|
||||
self.prices[item],
|
||||
]
|
||||
|
||||
@overrides
|
||||
def get_variable_category(self, var_name: VariableName) -> Optional[Category]:
|
||||
if var_name.startswith("x"):
|
||||
return "default"
|
||||
return None
|
||||
|
||||
Reference in New Issue
Block a user