ConstraintFeatures: Store lhs and sense

master
Alinson S. Xavier 5 years ago
parent 3a60deac63
commit b6ea0c5f1b

@ -2,9 +2,9 @@
# Copyright (C) 2020, UChicago Argonne, LLC. All rights reserved. # Copyright (C) 2020, UChicago Argonne, LLC. All rights reserved.
# Released under the modified BSD license. See COPYING.md for more details. # Released under the modified BSD license. See COPYING.md for more details.
from typing import TYPE_CHECKING from typing import TYPE_CHECKING, Dict
from miplearn.types import ModelFeatures from miplearn.types import ModelFeatures, ConstraintFeatures
if TYPE_CHECKING: if TYPE_CHECKING:
from miplearn import InternalSolver from miplearn import InternalSolver
@ -15,12 +15,16 @@ class ModelFeaturesExtractor:
self, self,
internal_solver: "InternalSolver", internal_solver: "InternalSolver",
) -> None: ) -> None:
self.internal_solver = internal_solver self.solver = internal_solver
def extract(self) -> ModelFeatures: def extract(self) -> ModelFeatures:
rhs = {} constraints: Dict[str, ConstraintFeatures] = {}
for cid in self.internal_solver.get_constraint_ids(): for cid in self.solver.get_constraint_ids():
rhs[cid] = self.internal_solver.get_constraint_rhs(cid) constraints[cid] = {
"rhs": self.solver.get_constraint_rhs(cid),
"lhs": self.solver.get_constraint_lhs(cid),
"sense": self.solver.get_constraint_sense(cid),
}
return { return {
"ConstraintRHS": rhs, "constraints": constraints,
} }

@ -339,6 +339,15 @@ class GurobiSolver(InternalSolver):
assert self.model is not None assert self.model is not None
return self.model.getConstrByName(cid).rhs return self.model.getConstrByName(cid).rhs
def get_constraint_lhs(self, cid: str) -> Dict[str, float]:
assert self.model is not None
constr = self.model.getConstrByName(cid)
expr = self.model.getRow(constr)
lhs: Dict[str, float] = {}
for i in range(expr.size()):
lhs[expr.getVar(i).varName] = expr.getCoeff(i)
return lhs
def extract_constraint(self, cid): def extract_constraint(self, cid):
self._raise_if_callback() self._raise_if_callback()
constr = self.model.getConstrByName(cid) constr = self.model.getConstrByName(cid)

@ -4,7 +4,7 @@
import logging import logging
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from typing import Any, Dict, List, Optional from typing import Any, Dict, List, Optional, Tuple
from miplearn.instance import Instance from miplearn.instance import Instance
from miplearn.types import ( from miplearn.types import (
@ -162,6 +162,17 @@ class InternalSolver(ABC):
""" """
pass pass
@abstractmethod
def get_constraint_lhs(self, cid: str) -> Dict[str, float]:
"""
Returns a list of tuples encoding the left-hand side of the constraint.
The first element of the tuple is the name of the variable and the second
element is the coefficient. For example, the left-hand side of "2 x1 + x2 <= 3"
is encoded as [{"x1": 2, "x2": 1}].
"""
pass
@abstractmethod @abstractmethod
def add_constraint(self, cobj: Constraint) -> None: def add_constraint(self, cobj: Constraint) -> None:
""" """

@ -298,7 +298,9 @@ class BasePyomoSolver(InternalSolver):
cobj = self._cname_to_constr[cid] cobj = self._cname_to_constr[cid]
has_ub = cobj.has_ub() has_ub = cobj.has_ub()
has_lb = cobj.has_lb() has_lb = cobj.has_lb()
assert (not has_lb) or (not has_ub), "range constraints not supported" assert (
(not has_lb) or (not has_ub) or cobj.upper() == cobj.lower()
), "range constraints not supported"
if has_lb: if has_lb:
return ">" return ">"
elif has_ub: elif has_ub:
@ -313,6 +315,9 @@ class BasePyomoSolver(InternalSolver):
else: else:
return cobj.lower() return cobj.lower()
def get_constraint_lhs(self, cid: str) -> Dict[str, float]:
return {}
def set_constraint_sense(self, cid: str, sense: str) -> None: def set_constraint_sense(self, cid: str, sense: str) -> None:
raise Exception("Not implemented") raise Exception("Not implemented")

@ -2,7 +2,7 @@
# Copyright (C) 2020, UChicago Argonne, LLC. All rights reserved. # Copyright (C) 2020, UChicago Argonne, LLC. All rights reserved.
# Released under the modified BSD license. See COPYING.md for more details. # Released under the modified BSD license. See COPYING.md for more details.
from typing import Optional, Dict, Callable, Any, Union, Tuple from typing import Optional, Dict, Callable, Any, Union, Tuple, List
from mypy_extensions import TypedDict from mypy_extensions import TypedDict
@ -71,10 +71,20 @@ LearningSolveStats = TypedDict(
total=False, total=False,
) )
ConstraintFeatures = TypedDict(
"ConstraintFeatures",
{
"rhs": float,
"lhs": Dict[str, float],
"sense": str,
},
total=False,
)
ModelFeatures = TypedDict( ModelFeatures = TypedDict(
"ModelFeatures", "ModelFeatures",
{ {
"ConstraintRHS": Dict[str, float], "constraints": Dict[str, ConstraintFeatures],
}, },
total=False, total=False,
) )

@ -1,13 +1,14 @@
# MIPLearn: Extensible Framework for Learning-Enhanced Mixed-Integer Optimization # MIPLearn: Extensible Framework for Learning-Enhanced Mixed-Integer Optimization
# Copyright (C) 2020, UChicago Argonne, LLC. All rights reserved. # Copyright (C) 2020, UChicago Argonne, LLC. All rights reserved.
# Released under the modified BSD license. See COPYING.md for more details. # Released under the modified BSD license. See COPYING.md for more details.
from miplearn import GurobiSolver
from miplearn.features import ModelFeaturesExtractor from miplearn.features import ModelFeaturesExtractor
from tests.fixtures.knapsack import get_knapsack_instance from tests.fixtures.knapsack import get_knapsack_instance
from tests.solvers import get_internal_solvers
def test_knapsack() -> None: def test_knapsack() -> None:
for solver_factory in get_internal_solvers(): for solver_factory in [GurobiSolver]:
# Initialize model, instance and internal solver # Initialize model, instance and internal solver
solver = solver_factory() solver = solver_factory()
instance = get_knapsack_instance(solver) instance = get_knapsack_instance(solver)
@ -20,4 +21,11 @@ def test_knapsack() -> None:
# Test constraint features # Test constraint features
print(solver, features) print(solver, features)
assert features["ConstraintRHS"]["eq_capacity"] == 67.0 assert features["constraints"]["eq_capacity"]["lhs"] == {
"x[0]": 23.0,
"x[1]": 26.0,
"x[2]": 20.0,
"x[3]": 18.0,
}
assert features["constraints"]["eq_capacity"]["sense"] == "<"
assert features["constraints"]["eq_capacity"]["rhs"] == 67.0

Loading…
Cancel
Save