mirror of
https://github.com/ANL-CEEESA/MIPLearn.git
synced 2025-12-06 09:28:51 -06:00
Remove tuples from VariableFeatures
This commit is contained in:
@@ -30,22 +30,22 @@ class InstanceFeatures:
|
|||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class VariableFeatures:
|
class VariableFeatures:
|
||||||
names: Optional[Tuple[str, ...]] = None
|
names: Optional[List[str]] = None
|
||||||
basis_status: Optional[Tuple[str, ...]] = None
|
basis_status: Optional[List[str]] = None
|
||||||
categories: Optional[Tuple[Optional[Hashable], ...]] = None
|
categories: Optional[List[Optional[Hashable]]] = None
|
||||||
lower_bounds: Optional[Tuple[float, ...]] = None
|
lower_bounds: Optional[List[float]] = None
|
||||||
obj_coeffs: Optional[Tuple[float, ...]] = None
|
obj_coeffs: Optional[List[float]] = None
|
||||||
reduced_costs: Optional[Tuple[float, ...]] = None
|
reduced_costs: Optional[List[float]] = None
|
||||||
sa_lb_down: Optional[Tuple[float, ...]] = None
|
sa_lb_down: Optional[List[float]] = None
|
||||||
sa_lb_up: Optional[Tuple[float, ...]] = None
|
sa_lb_up: Optional[List[float]] = None
|
||||||
sa_obj_down: Optional[Tuple[float, ...]] = None
|
sa_obj_down: Optional[List[float]] = None
|
||||||
sa_obj_up: Optional[Tuple[float, ...]] = None
|
sa_obj_up: Optional[List[float]] = None
|
||||||
sa_ub_down: Optional[Tuple[float, ...]] = None
|
sa_ub_down: Optional[List[float]] = None
|
||||||
sa_ub_up: Optional[Tuple[float, ...]] = None
|
sa_ub_up: Optional[List[float]] = None
|
||||||
types: Optional[Tuple[str, ...]] = None
|
types: Optional[List[str]] = None
|
||||||
upper_bounds: Optional[Tuple[float, ...]] = None
|
upper_bounds: Optional[List[float]] = None
|
||||||
user_features: Optional[Tuple[Optional[Tuple[float, ...]], ...]] = None
|
user_features: Optional[List[Optional[List[float]]]] = None
|
||||||
values: Optional[Tuple[float, ...]] = None
|
values: Optional[List[float]] = None
|
||||||
|
|
||||||
# Alvarez, A. M., Louveaux, Q., & Wehenkel, L. (2017). A machine learning-based
|
# Alvarez, A. M., Louveaux, Q., & Wehenkel, L. (2017). A machine learning-based
|
||||||
# approximation of strong branching. INFORMS Journal on Computing, 29(1), 185-195.
|
# approximation of strong branching. INFORMS Journal on Computing, 29(1), 185-195.
|
||||||
@@ -190,7 +190,7 @@ class FeaturesExtractor:
|
|||||||
assert features.variables is not None
|
assert features.variables is not None
|
||||||
assert features.variables.names is not None
|
assert features.variables.names is not None
|
||||||
categories: List[Hashable] = []
|
categories: List[Hashable] = []
|
||||||
user_features: List[Optional[Tuple[float, ...]]] = []
|
user_features: List[Optional[List[float]]] = []
|
||||||
for (i, var_name) in enumerate(features.variables.names):
|
for (i, var_name) in enumerate(features.variables.names):
|
||||||
category: Hashable = instance.get_variable_category(var_name)
|
category: Hashable = instance.get_variable_category(var_name)
|
||||||
user_features_i: Optional[List[float]] = None
|
user_features_i: Optional[List[float]] = None
|
||||||
@@ -217,9 +217,9 @@ class FeaturesExtractor:
|
|||||||
if user_features_i is None:
|
if user_features_i is None:
|
||||||
user_features.append(None)
|
user_features.append(None)
|
||||||
else:
|
else:
|
||||||
user_features.append(tuple(user_features_i))
|
user_features.append(user_features_i)
|
||||||
features.variables.categories = tuple(categories)
|
features.variables.categories = categories
|
||||||
features.variables.user_features = tuple(user_features)
|
features.variables.user_features = user_features
|
||||||
|
|
||||||
def _extract_user_features_constrs(
|
def _extract_user_features_constrs(
|
||||||
self,
|
self,
|
||||||
|
|||||||
@@ -75,12 +75,12 @@ class GurobiSolver(InternalSolver):
|
|||||||
self._cname_to_constr: Dict[str, "gurobipy.Constr"] = {}
|
self._cname_to_constr: Dict[str, "gurobipy.Constr"] = {}
|
||||||
self._gp_vars: Tuple["gurobipy.Var", ...] = tuple()
|
self._gp_vars: Tuple["gurobipy.Var", ...] = tuple()
|
||||||
self._gp_constrs: Tuple["gurobipy.Constr", ...] = tuple()
|
self._gp_constrs: Tuple["gurobipy.Constr", ...] = tuple()
|
||||||
self._var_names: Tuple[str, ...] = tuple()
|
self._var_names: List[str] = []
|
||||||
self._constr_names: Tuple[str, ...] = tuple()
|
self._constr_names: Tuple[str, ...] = tuple()
|
||||||
self._var_types: Tuple[str, ...] = tuple()
|
self._var_types: List[str] = []
|
||||||
self._var_lbs: Tuple[float, ...] = tuple()
|
self._var_lbs: List[float] = []
|
||||||
self._var_ubs: Tuple[float, ...] = tuple()
|
self._var_ubs: List[float] = []
|
||||||
self._var_obj_coeffs: Tuple[float, ...] = tuple()
|
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]
|
||||||
@@ -328,8 +328,8 @@ class GurobiSolver(InternalSolver):
|
|||||||
obj_coeffs = self._var_obj_coeffs
|
obj_coeffs = self._var_obj_coeffs
|
||||||
|
|
||||||
if self._has_lp_solution:
|
if self._has_lp_solution:
|
||||||
reduced_costs = tuple(model.getAttr("rc", self._gp_vars))
|
reduced_costs = model.getAttr("rc", self._gp_vars)
|
||||||
basis_status = tuple(
|
basis_status = list(
|
||||||
map(
|
map(
|
||||||
_parse_gurobi_vbasis,
|
_parse_gurobi_vbasis,
|
||||||
model.getAttr("vbasis", self._gp_vars),
|
model.getAttr("vbasis", self._gp_vars),
|
||||||
@@ -337,15 +337,15 @@ class GurobiSolver(InternalSolver):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if with_sa:
|
if with_sa:
|
||||||
sa_obj_up = tuple(model.getAttr("saobjUp", self._gp_vars))
|
sa_obj_up = model.getAttr("saobjUp", self._gp_vars)
|
||||||
sa_obj_down = tuple(model.getAttr("saobjLow", self._gp_vars))
|
sa_obj_down = model.getAttr("saobjLow", self._gp_vars)
|
||||||
sa_ub_up = tuple(model.getAttr("saubUp", self._gp_vars))
|
sa_ub_up = model.getAttr("saubUp", self._gp_vars)
|
||||||
sa_ub_down = tuple(model.getAttr("saubLow", self._gp_vars))
|
sa_ub_down = model.getAttr("saubLow", self._gp_vars)
|
||||||
sa_lb_up = tuple(model.getAttr("salbUp", self._gp_vars))
|
sa_lb_up = model.getAttr("salbUp", self._gp_vars)
|
||||||
sa_lb_down = tuple(model.getAttr("salbLow", self._gp_vars))
|
sa_lb_down = model.getAttr("salbLow", self._gp_vars)
|
||||||
|
|
||||||
if model.solCount > 0:
|
if model.solCount > 0:
|
||||||
values = tuple(model.getAttr("x", self._gp_vars))
|
values = model.getAttr("x", self._gp_vars)
|
||||||
|
|
||||||
return VariableFeatures(
|
return VariableFeatures(
|
||||||
names=self._var_names,
|
names=self._var_names,
|
||||||
@@ -600,12 +600,12 @@ class GurobiSolver(InternalSolver):
|
|||||||
self._cname_to_constr = cname_to_constr
|
self._cname_to_constr = cname_to_constr
|
||||||
self._gp_vars = tuple(gp_vars)
|
self._gp_vars = tuple(gp_vars)
|
||||||
self._gp_constrs = tuple(gp_constrs)
|
self._gp_constrs = tuple(gp_constrs)
|
||||||
self._var_names = tuple(var_names)
|
self._var_names = var_names
|
||||||
self._constr_names = tuple(constr_names)
|
self._constr_names = tuple(constr_names)
|
||||||
self._var_types = tuple(var_types)
|
self._var_types = var_types
|
||||||
self._var_lbs = tuple(var_lbs)
|
self._var_lbs = var_lbs
|
||||||
self._var_ubs = tuple(var_ubs)
|
self._var_ubs = var_ubs
|
||||||
self._var_obj_coeffs = tuple(var_obj_coeffs)
|
self._var_obj_coeffs = var_obj_coeffs
|
||||||
|
|
||||||
def __getstate__(self) -> Dict:
|
def __getstate__(self) -> Dict:
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -337,33 +337,20 @@ class BasePyomoSolver(InternalSolver):
|
|||||||
if self._has_lp_solution or self._has_mip_solution:
|
if self._has_lp_solution or self._has_mip_solution:
|
||||||
values.append(v.value)
|
values.append(v.value)
|
||||||
|
|
||||||
types_t: Optional[Tuple[str, ...]] = None
|
def _none_if_empty(obj: Any) -> Any:
|
||||||
upper_bounds_t: Optional[Tuple[float, ...]] = None
|
if len(obj) == 0:
|
||||||
lower_bounds_t: Optional[Tuple[float, ...]] = None
|
return None
|
||||||
obj_coeffs_t: Optional[Tuple[float, ...]] = None
|
else:
|
||||||
reduced_costs_t: Optional[Tuple[float, ...]] = None
|
return obj
|
||||||
values_t: Optional[Tuple[float, ...]] = None
|
|
||||||
|
|
||||||
if with_static:
|
|
||||||
types_t = tuple(types)
|
|
||||||
upper_bounds_t = tuple(upper_bounds)
|
|
||||||
lower_bounds_t = tuple(lower_bounds)
|
|
||||||
obj_coeffs_t = tuple(obj_coeffs)
|
|
||||||
|
|
||||||
if self._has_lp_solution:
|
|
||||||
reduced_costs_t = tuple(reduced_costs)
|
|
||||||
|
|
||||||
if self._has_lp_solution or self._has_mip_solution:
|
|
||||||
values_t = tuple(values)
|
|
||||||
|
|
||||||
return VariableFeatures(
|
return VariableFeatures(
|
||||||
names=tuple(names),
|
names=_none_if_empty(names),
|
||||||
types=types_t,
|
types=_none_if_empty(types),
|
||||||
upper_bounds=upper_bounds_t,
|
upper_bounds=_none_if_empty(upper_bounds),
|
||||||
lower_bounds=lower_bounds_t,
|
lower_bounds=_none_if_empty(lower_bounds),
|
||||||
obj_coeffs=obj_coeffs_t,
|
obj_coeffs=_none_if_empty(obj_coeffs),
|
||||||
reduced_costs=reduced_costs_t,
|
reduced_costs=_none_if_empty(reduced_costs),
|
||||||
values=values_t,
|
values=_none_if_empty(values),
|
||||||
)
|
)
|
||||||
|
|
||||||
@overrides
|
@overrides
|
||||||
|
|||||||
@@ -57,11 +57,11 @@ def run_basic_usage_tests(solver: InternalSolver) -> None:
|
|||||||
assert_equals(
|
assert_equals(
|
||||||
solver.get_variables(),
|
solver.get_variables(),
|
||||||
VariableFeatures(
|
VariableFeatures(
|
||||||
names=("x[0]", "x[1]", "x[2]", "x[3]", "z"),
|
names=["x[0]", "x[1]", "x[2]", "x[3]", "z"],
|
||||||
lower_bounds=(0.0, 0.0, 0.0, 0.0, 0.0),
|
lower_bounds=[0.0, 0.0, 0.0, 0.0, 0.0],
|
||||||
upper_bounds=(1.0, 1.0, 1.0, 1.0, 67.0),
|
upper_bounds=[1.0, 1.0, 1.0, 1.0, 67.0],
|
||||||
types=("B", "B", "B", "B", "C"),
|
types=["B", "B", "B", "B", "C"],
|
||||||
obj_coeffs=(505.0, 352.0, 458.0, 220.0, 0.0),
|
obj_coeffs=[505.0, 352.0, 458.0, 220.0, 0.0],
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -100,16 +100,16 @@ def run_basic_usage_tests(solver: InternalSolver) -> None:
|
|||||||
_filter_attrs(
|
_filter_attrs(
|
||||||
solver.get_variable_attrs(),
|
solver.get_variable_attrs(),
|
||||||
VariableFeatures(
|
VariableFeatures(
|
||||||
names=("x[0]", "x[1]", "x[2]", "x[3]", "z"),
|
names=["x[0]", "x[1]", "x[2]", "x[3]", "z"],
|
||||||
basis_status=("U", "B", "U", "L", "U"),
|
basis_status=["U", "B", "U", "L", "U"],
|
||||||
reduced_costs=(193.615385, 0.0, 187.230769, -23.692308, 13.538462),
|
reduced_costs=[193.615385, 0.0, 187.230769, -23.692308, 13.538462],
|
||||||
sa_lb_down=(-inf, -inf, -inf, -0.111111, -inf),
|
sa_lb_down=[-inf, -inf, -inf, -0.111111, -inf],
|
||||||
sa_lb_up=(1.0, 0.923077, 1.0, 1.0, 67.0),
|
sa_lb_up=[1.0, 0.923077, 1.0, 1.0, 67.0],
|
||||||
sa_obj_down=(311.384615, 317.777778, 270.769231, -inf, -13.538462),
|
sa_obj_down=[311.384615, 317.777778, 270.769231, -inf, -13.538462],
|
||||||
sa_obj_up=(inf, 570.869565, inf, 243.692308, inf),
|
sa_obj_up=[inf, 570.869565, inf, 243.692308, inf],
|
||||||
sa_ub_down=(0.913043, 0.923077, 0.9, 0.0, 43.0),
|
sa_ub_down=[0.913043, 0.923077, 0.9, 0.0, 43.0],
|
||||||
sa_ub_up=(2.043478, inf, 2.2, inf, 69.0),
|
sa_ub_up=[2.043478, inf, 2.2, inf, 69.0],
|
||||||
values=(1.0, 0.923077, 1.0, 0.0, 67.0),
|
values=[1.0, 0.923077, 1.0, 0.0, 67.0],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -153,8 +153,8 @@ def run_basic_usage_tests(solver: InternalSolver) -> None:
|
|||||||
_filter_attrs(
|
_filter_attrs(
|
||||||
solver.get_variable_attrs(),
|
solver.get_variable_attrs(),
|
||||||
VariableFeatures(
|
VariableFeatures(
|
||||||
names=("x[0]", "x[1]", "x[2]", "x[3]", "z"),
|
names=["x[0]", "x[1]", "x[2]", "x[3]", "z"],
|
||||||
values=(1.0, 0.0, 1.0, 1.0, 61.0),
|
values=[1.0, 0.0, 1.0, 1.0, 61.0],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -29,8 +29,8 @@ def sample() -> Sample:
|
|||||||
after_load=Features(
|
after_load=Features(
|
||||||
instance=InstanceFeatures(),
|
instance=InstanceFeatures(),
|
||||||
variables=VariableFeatures(
|
variables=VariableFeatures(
|
||||||
names=("x[0]", "x[1]", "x[2]", "x[3]"),
|
names=["x[0]", "x[1]", "x[2]", "x[3]"],
|
||||||
categories=("default", None, "default", "default"),
|
categories=["default", None, "default", "default"],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
after_lp=Features(
|
after_lp=Features(
|
||||||
@@ -38,8 +38,8 @@ def sample() -> Sample:
|
|||||||
),
|
),
|
||||||
after_mip=Features(
|
after_mip=Features(
|
||||||
variables=VariableFeatures(
|
variables=VariableFeatures(
|
||||||
names=("x[0]", "x[1]", "x[2]", "x[3]"),
|
names=["x[0]", "x[1]", "x[2]", "x[3]"],
|
||||||
values=(0.0, 1.0, 1.0, 0.0),
|
values=[0.0, 1.0, 1.0, 0.0],
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ def test_instance() -> None:
|
|||||||
features = instance.samples[0].after_mip
|
features = instance.samples[0].after_mip
|
||||||
assert features is not None
|
assert features is not None
|
||||||
assert features.variables is not None
|
assert features.variables is not None
|
||||||
assert features.variables.values == (1.0, 0.0, 1.0, 1.0, 0.0, 1.0)
|
assert features.variables.values == [1.0, 0.0, 1.0, 1.0, 0.0, 1.0]
|
||||||
assert features.mip_solve is not None
|
assert features.mip_solve is not None
|
||||||
assert features.mip_solve.mip_lower_bound == 4.0
|
assert features.mip_solve.mip_lower_bound == 4.0
|
||||||
assert features.mip_solve.mip_upper_bound == 4.0
|
assert features.mip_solve.mip_upper_bound == 4.0
|
||||||
@@ -75,7 +75,7 @@ def test_subtour() -> None:
|
|||||||
assert lazy_enforced is not None
|
assert lazy_enforced is not None
|
||||||
assert len(lazy_enforced) > 0
|
assert len(lazy_enforced) > 0
|
||||||
assert features.variables is not None
|
assert features.variables is not None
|
||||||
assert features.variables.values == (
|
assert features.variables.values == [
|
||||||
1.0,
|
1.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
@@ -91,6 +91,6 @@ def test_subtour() -> None:
|
|||||||
0.0,
|
0.0,
|
||||||
1.0,
|
1.0,
|
||||||
1.0,
|
1.0,
|
||||||
)
|
]
|
||||||
solver.fit([instance])
|
solver.fit([instance])
|
||||||
solver.solve(instance)
|
solver.solve(instance)
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ def test_learning_solver(
|
|||||||
after_mip = sample.after_mip
|
after_mip = sample.after_mip
|
||||||
assert after_mip is not None
|
assert after_mip is not None
|
||||||
assert after_mip.variables is not None
|
assert after_mip.variables is not None
|
||||||
assert after_mip.variables.values == (1.0, 0.0, 1.0, 1.0, 61.0)
|
assert after_mip.variables.values == [1.0, 0.0, 1.0, 1.0, 61.0]
|
||||||
assert after_mip.mip_solve is not None
|
assert after_mip.mip_solve is not None
|
||||||
assert after_mip.mip_solve.mip_lower_bound == 1183.0
|
assert after_mip.mip_solve.mip_lower_bound == 1183.0
|
||||||
assert after_mip.mip_solve.mip_upper_bound == 1183.0
|
assert after_mip.mip_solve.mip_upper_bound == 1183.0
|
||||||
@@ -51,7 +51,7 @@ def test_learning_solver(
|
|||||||
after_lp = sample.after_lp
|
after_lp = sample.after_lp
|
||||||
assert after_lp is not None
|
assert after_lp is not None
|
||||||
assert after_lp.variables is not None
|
assert after_lp.variables is not None
|
||||||
assert _round(after_lp.variables.values) == (1.0, 0.923077, 1.0, 0.0, 67.0)
|
assert _round(after_lp.variables.values) == [1.0, 0.923077, 1.0, 0.0, 67.0]
|
||||||
assert after_lp.lp_solve is not None
|
assert after_lp.lp_solve is not None
|
||||||
assert after_lp.lp_solve.lp_value is not None
|
assert after_lp.lp_solve.lp_value is not None
|
||||||
assert round(after_lp.lp_solve.lp_value, 3) == 1287.923
|
assert round(after_lp.lp_solve.lp_value, 3) == 1287.923
|
||||||
|
|||||||
@@ -31,28 +31,28 @@ def test_knapsack() -> None:
|
|||||||
assert_equals(
|
assert_equals(
|
||||||
_round(features.variables),
|
_round(features.variables),
|
||||||
VariableFeatures(
|
VariableFeatures(
|
||||||
names=("x[0]", "x[1]", "x[2]", "x[3]", "z"),
|
names=["x[0]", "x[1]", "x[2]", "x[3]", "z"],
|
||||||
basis_status=("U", "B", "U", "L", "U"),
|
basis_status=["U", "B", "U", "L", "U"],
|
||||||
categories=("default", "default", "default", "default", None),
|
categories=["default", "default", "default", "default", None],
|
||||||
lower_bounds=(0.0, 0.0, 0.0, 0.0, 0.0),
|
lower_bounds=[0.0, 0.0, 0.0, 0.0, 0.0],
|
||||||
obj_coeffs=(505.0, 352.0, 458.0, 220.0, 0.0),
|
obj_coeffs=[505.0, 352.0, 458.0, 220.0, 0.0],
|
||||||
reduced_costs=(193.615385, 0.0, 187.230769, -23.692308, 13.538462),
|
reduced_costs=[193.615385, 0.0, 187.230769, -23.692308, 13.538462],
|
||||||
sa_lb_down=(-inf, -inf, -inf, -0.111111, -inf),
|
sa_lb_down=[-inf, -inf, -inf, -0.111111, -inf],
|
||||||
sa_lb_up=(1.0, 0.923077, 1.0, 1.0, 67.0),
|
sa_lb_up=[1.0, 0.923077, 1.0, 1.0, 67.0],
|
||||||
sa_obj_down=(311.384615, 317.777778, 270.769231, -inf, -13.538462),
|
sa_obj_down=[311.384615, 317.777778, 270.769231, -inf, -13.538462],
|
||||||
sa_obj_up=(inf, 570.869565, inf, 243.692308, inf),
|
sa_obj_up=[inf, 570.869565, inf, 243.692308, inf],
|
||||||
sa_ub_down=(0.913043, 0.923077, 0.9, 0.0, 43.0),
|
sa_ub_down=[0.913043, 0.923077, 0.9, 0.0, 43.0],
|
||||||
sa_ub_up=(2.043478, inf, 2.2, inf, 69.0),
|
sa_ub_up=[2.043478, inf, 2.2, inf, 69.0],
|
||||||
types=("B", "B", "B", "B", "C"),
|
types=["B", "B", "B", "B", "C"],
|
||||||
upper_bounds=(1.0, 1.0, 1.0, 1.0, 67.0),
|
upper_bounds=[1.0, 1.0, 1.0, 1.0, 67.0],
|
||||||
user_features=(
|
user_features=[
|
||||||
(23.0, 505.0),
|
[23.0, 505.0],
|
||||||
(26.0, 352.0),
|
[26.0, 352.0],
|
||||||
(20.0, 458.0),
|
[20.0, 458.0],
|
||||||
(18.0, 220.0),
|
[18.0, 220.0],
|
||||||
None,
|
None,
|
||||||
),
|
],
|
||||||
values=(1.0, 0.923077, 1.0, 0.0, 67.0),
|
values=[1.0, 0.923077, 1.0, 0.0, 67.0],
|
||||||
alvarez_2017=[
|
alvarez_2017=[
|
||||||
[1.0, 0.32899, 0.0, 0.0, 1.0, 1.0, 5.265874, 46.051702],
|
[1.0, 0.32899, 0.0, 0.0, 1.0, 1.0, 5.265874, 46.051702],
|
||||||
[1.0, 0.229316, 0.0, 0.076923, 1.0, 1.0, 3.532875, 5.388476],
|
[1.0, 0.229316, 0.0, 0.076923, 1.0, 1.0, 3.532875, 5.388476],
|
||||||
|
|||||||
Reference in New Issue
Block a user