mirror of
https://github.com/ANL-CEEESA/MIPLearn.git
synced 2025-12-06 09:28:51 -06:00
Remove tuples from ConstraintFeatures
This commit is contained in:
@@ -88,10 +88,10 @@ class StaticLazyConstraintsComponent(Component):
|
|||||||
assert constraints is not None
|
assert constraints is not None
|
||||||
assert constraints.lazy is not None
|
assert constraints.lazy is not None
|
||||||
assert constraints.names is not None
|
assert constraints.names is not None
|
||||||
selected = tuple(
|
selected = [
|
||||||
(constraints.lazy[i] and constraints.names[i] not in self.enforced_cids)
|
(constraints.lazy[i] and constraints.names[i] not in self.enforced_cids)
|
||||||
for i in range(len(constraints.lazy))
|
for i in range(len(constraints.lazy))
|
||||||
)
|
]
|
||||||
n_removed = sum(selected)
|
n_removed = sum(selected)
|
||||||
n_kept = sum(constraints.lazy) - n_removed
|
n_kept = sum(constraints.lazy) - n_removed
|
||||||
self.pool = constraints[selected]
|
self.pool = constraints[selected]
|
||||||
@@ -174,7 +174,7 @@ class StaticLazyConstraintsComponent(Component):
|
|||||||
self.pool,
|
self.pool,
|
||||||
tol=self.violation_tolerance,
|
tol=self.violation_tolerance,
|
||||||
)
|
)
|
||||||
is_violated = tuple(not i for i in is_satisfied)
|
is_violated = [not i for i in is_satisfied]
|
||||||
violated_constraints = self.pool[is_violated]
|
violated_constraints = self.pool[is_violated]
|
||||||
satisfied_constraints = self.pool[is_satisfied]
|
satisfied_constraints = self.pool[is_satisfied]
|
||||||
self.pool = satisfied_constraints
|
self.pool = satisfied_constraints
|
||||||
|
|||||||
@@ -78,18 +78,18 @@ class VariableFeatures:
|
|||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class ConstraintFeatures:
|
class ConstraintFeatures:
|
||||||
basis_status: Optional[Tuple[str, ...]] = None
|
basis_status: Optional[List[str]] = None
|
||||||
categories: Optional[Tuple[Optional[Hashable], ...]] = None
|
categories: Optional[List[Optional[Hashable]]] = None
|
||||||
dual_values: Optional[Tuple[float, ...]] = None
|
dual_values: Optional[List[float]] = None
|
||||||
names: Optional[Tuple[str, ...]] = None
|
names: Optional[List[str]] = None
|
||||||
lazy: Optional[Tuple[bool, ...]] = None
|
lazy: Optional[List[bool]] = None
|
||||||
lhs: Optional[Tuple[Tuple[Tuple[str, float], ...], ...]] = None
|
lhs: Optional[List[List[Tuple[str, float]]]] = None
|
||||||
rhs: Optional[Tuple[float, ...]] = None
|
rhs: Optional[List[float]] = None
|
||||||
sa_rhs_down: Optional[Tuple[float, ...]] = None
|
sa_rhs_down: Optional[List[float]] = None
|
||||||
sa_rhs_up: Optional[Tuple[float, ...]] = None
|
sa_rhs_up: Optional[List[float]] = None
|
||||||
senses: Optional[Tuple[str, ...]] = None
|
senses: Optional[List[str]] = None
|
||||||
slacks: Optional[Tuple[float, ...]] = None
|
slacks: Optional[List[float]] = None
|
||||||
user_features: Optional[Tuple[Optional[Tuple[float, ...]], ...]] = None
|
user_features: Optional[List[Optional[List[float]]]] = None
|
||||||
|
|
||||||
def to_list(self, index: int) -> List[float]:
|
def to_list(self, index: int) -> List[float]:
|
||||||
features: List[float] = []
|
features: List[float] = []
|
||||||
@@ -107,7 +107,7 @@ class ConstraintFeatures:
|
|||||||
_clip(features)
|
_clip(features)
|
||||||
return features
|
return features
|
||||||
|
|
||||||
def __getitem__(self, selected: Tuple[bool, ...]) -> "ConstraintFeatures":
|
def __getitem__(self, selected: List[bool]) -> "ConstraintFeatures":
|
||||||
return ConstraintFeatures(
|
return ConstraintFeatures(
|
||||||
basis_status=self._filter(self.basis_status, selected),
|
basis_status=self._filter(self.basis_status, selected),
|
||||||
categories=self._filter(self.categories, selected),
|
categories=self._filter(self.categories, selected),
|
||||||
@@ -125,12 +125,12 @@ class ConstraintFeatures:
|
|||||||
|
|
||||||
def _filter(
|
def _filter(
|
||||||
self,
|
self,
|
||||||
obj: Optional[Tuple],
|
obj: Optional[List],
|
||||||
selected: Tuple[bool, ...],
|
selected: List[bool],
|
||||||
) -> Optional[Tuple]:
|
) -> Optional[List]:
|
||||||
if obj is None:
|
if obj is None:
|
||||||
return None
|
return None
|
||||||
return tuple(obj[i] for (i, selected_i) in enumerate(selected) if selected_i)
|
return [obj[i] for (i, selected_i) in enumerate(selected) if selected_i]
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@@ -217,7 +217,7 @@ 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(user_features_i)
|
user_features.append(list(user_features_i))
|
||||||
features.variables.categories = categories
|
features.variables.categories = categories
|
||||||
features.variables.user_features = user_features
|
features.variables.user_features = user_features
|
||||||
|
|
||||||
@@ -229,7 +229,7 @@ class FeaturesExtractor:
|
|||||||
assert features.constraints is not None
|
assert features.constraints is not None
|
||||||
assert features.constraints.names is not None
|
assert features.constraints.names is not None
|
||||||
has_static_lazy = instance.has_static_lazy_constraints()
|
has_static_lazy = instance.has_static_lazy_constraints()
|
||||||
user_features: List[Optional[Tuple[float, ...]]] = []
|
user_features: List[Optional[List[float]]] = []
|
||||||
categories: List[Optional[Hashable]] = []
|
categories: List[Optional[Hashable]] = []
|
||||||
lazy: List[bool] = []
|
lazy: List[bool] = []
|
||||||
for (cidx, cname) in enumerate(features.constraints.names):
|
for (cidx, cname) in enumerate(features.constraints.names):
|
||||||
@@ -253,7 +253,7 @@ class FeaturesExtractor:
|
|||||||
f"Constraint features must be a list of numbers. "
|
f"Constraint features must be a list of numbers. "
|
||||||
f"Found {type(f).__name__} instead for cname={cname}."
|
f"Found {type(f).__name__} instead for cname={cname}."
|
||||||
)
|
)
|
||||||
user_features.append(tuple(cf))
|
user_features.append(list(cf))
|
||||||
else:
|
else:
|
||||||
user_features.append(None)
|
user_features.append(None)
|
||||||
categories.append(None)
|
categories.append(None)
|
||||||
@@ -261,9 +261,9 @@ class FeaturesExtractor:
|
|||||||
lazy.append(instance.is_constraint_lazy(cname))
|
lazy.append(instance.is_constraint_lazy(cname))
|
||||||
else:
|
else:
|
||||||
lazy.append(False)
|
lazy.append(False)
|
||||||
features.constraints.user_features = tuple(user_features)
|
features.constraints.user_features = user_features
|
||||||
features.constraints.lazy = tuple(lazy)
|
features.constraints.lazy = lazy
|
||||||
features.constraints.categories = tuple(categories)
|
features.constraints.categories = categories
|
||||||
|
|
||||||
def _extract_user_features_instance(
|
def _extract_user_features_instance(
|
||||||
self,
|
self,
|
||||||
|
|||||||
@@ -36,3 +36,10 @@ class _RedirectOutput:
|
|||||||
) -> None:
|
) -> None:
|
||||||
sys.stdout = self._original_stdout
|
sys.stdout = self._original_stdout
|
||||||
sys.stderr = self._original_stderr
|
sys.stderr = self._original_stderr
|
||||||
|
|
||||||
|
|
||||||
|
def _none_if_empty(obj: Any) -> Any:
|
||||||
|
if len(obj) == 0:
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
return obj
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ class GurobiSolver(InternalSolver):
|
|||||||
self,
|
self,
|
||||||
cf: ConstraintFeatures,
|
cf: ConstraintFeatures,
|
||||||
tol: float = 1e-5,
|
tol: float = 1e-5,
|
||||||
) -> Tuple[bool, ...]:
|
) -> List[bool]:
|
||||||
assert cf.names is not None
|
assert cf.names is not None
|
||||||
assert cf.senses is not None
|
assert cf.senses is not None
|
||||||
assert cf.lhs is not None
|
assert cf.lhs is not None
|
||||||
@@ -141,7 +141,7 @@ class GurobiSolver(InternalSolver):
|
|||||||
result.append(lhs >= cf.rhs[i] - tol)
|
result.append(lhs >= cf.rhs[i] - tol)
|
||||||
else:
|
else:
|
||||||
result.append(abs(cf.rhs[i] - lhs) <= tol)
|
result.append(abs(cf.rhs[i] - lhs) <= tol)
|
||||||
return tuple(result)
|
return result
|
||||||
|
|
||||||
@overrides
|
@overrides
|
||||||
def build_test_instance_infeasible(self) -> Instance:
|
def build_test_instance_infeasible(self) -> Instance:
|
||||||
@@ -209,37 +209,37 @@ class GurobiSolver(InternalSolver):
|
|||||||
raise Exception(f"unknown cbasis: {v}")
|
raise Exception(f"unknown cbasis: {v}")
|
||||||
|
|
||||||
gp_constrs = model.getConstrs()
|
gp_constrs = model.getConstrs()
|
||||||
constr_names = tuple(model.getAttr("constrName", gp_constrs))
|
constr_names = model.getAttr("constrName", gp_constrs)
|
||||||
rhs, lhs, senses, slacks, basis_status = None, None, None, None, None
|
lhs: Optional[List] = None
|
||||||
|
rhs, senses, slacks, basis_status = None, None, None, None
|
||||||
dual_value, basis_status, sa_rhs_up, sa_rhs_down = None, None, None, None
|
dual_value, basis_status, sa_rhs_up, sa_rhs_down = None, None, None, None
|
||||||
|
|
||||||
if with_static:
|
if with_static:
|
||||||
rhs = tuple(model.getAttr("rhs", gp_constrs))
|
rhs = model.getAttr("rhs", gp_constrs)
|
||||||
senses = tuple(model.getAttr("sense", gp_constrs))
|
senses = model.getAttr("sense", gp_constrs)
|
||||||
if with_lhs:
|
if with_lhs:
|
||||||
lhs_l: List = [None for _ in gp_constrs]
|
lhs = [None for _ in gp_constrs]
|
||||||
for (i, gp_constr) in enumerate(gp_constrs):
|
for (i, gp_constr) in enumerate(gp_constrs):
|
||||||
expr = model.getRow(gp_constr)
|
expr = model.getRow(gp_constr)
|
||||||
lhs_l[i] = tuple(
|
lhs[i] = [
|
||||||
(self._var_names[expr.getVar(j).index], expr.getCoeff(j))
|
(self._var_names[expr.getVar(j).index], expr.getCoeff(j))
|
||||||
for j in range(expr.size())
|
for j in range(expr.size())
|
||||||
)
|
]
|
||||||
lhs = tuple(lhs_l)
|
|
||||||
|
|
||||||
if self._has_lp_solution:
|
if self._has_lp_solution:
|
||||||
dual_value = tuple(model.getAttr("pi", gp_constrs))
|
dual_value = model.getAttr("pi", gp_constrs)
|
||||||
basis_status = tuple(
|
basis_status = list(
|
||||||
map(
|
map(
|
||||||
_parse_gurobi_cbasis,
|
_parse_gurobi_cbasis,
|
||||||
model.getAttr("cbasis", gp_constrs),
|
model.getAttr("cbasis", gp_constrs),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
if with_sa:
|
if with_sa:
|
||||||
sa_rhs_up = tuple(model.getAttr("saRhsUp", gp_constrs))
|
sa_rhs_up = model.getAttr("saRhsUp", gp_constrs)
|
||||||
sa_rhs_down = tuple(model.getAttr("saRhsLow", gp_constrs))
|
sa_rhs_down = model.getAttr("saRhsLow", gp_constrs)
|
||||||
|
|
||||||
if self._has_lp_solution or self._has_mip_solution:
|
if self._has_lp_solution or self._has_mip_solution:
|
||||||
slacks = tuple(model.getAttr("slack", gp_constrs))
|
slacks = model.getAttr("slack", gp_constrs)
|
||||||
|
|
||||||
return ConstraintFeatures(
|
return ConstraintFeatures(
|
||||||
basis_status=basis_status,
|
basis_status=basis_status,
|
||||||
@@ -370,7 +370,7 @@ class GurobiSolver(InternalSolver):
|
|||||||
return self.model.status in [self.gp.GRB.INFEASIBLE, self.gp.GRB.INF_OR_UNBD]
|
return self.model.status in [self.gp.GRB.INFEASIBLE, self.gp.GRB.INF_OR_UNBD]
|
||||||
|
|
||||||
@overrides
|
@overrides
|
||||||
def remove_constraints(self, names: Tuple[str, ...]) -> None:
|
def remove_constraints(self, names: List[str]) -> None:
|
||||||
assert self.model is not None
|
assert self.model is not None
|
||||||
constrs = [self.model.getConstrByName(n) for n in names]
|
constrs = [self.model.getConstrByName(n) for n in names]
|
||||||
self.model.remove(constrs)
|
self.model.remove(constrs)
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
import logging
|
import logging
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Any, List, Optional, Tuple
|
from typing import Any, List, Optional, List
|
||||||
|
|
||||||
from miplearn.features import VariableFeatures, ConstraintFeatures
|
from miplearn.features import VariableFeatures, ConstraintFeatures
|
||||||
from miplearn.instance.base import Instance
|
from miplearn.instance.base import Instance
|
||||||
@@ -59,7 +59,7 @@ class InternalSolver(ABC):
|
|||||||
self,
|
self,
|
||||||
cf: ConstraintFeatures,
|
cf: ConstraintFeatures,
|
||||||
tol: float = 1e-5,
|
tol: float = 1e-5,
|
||||||
) -> Tuple[bool, ...]:
|
) -> List[bool]:
|
||||||
"""
|
"""
|
||||||
Checks whether the current solution satisfies the given constraints.
|
Checks whether the current solution satisfies the given constraints.
|
||||||
"""
|
"""
|
||||||
@@ -176,7 +176,7 @@ class InternalSolver(ABC):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def remove_constraints(self, names: Tuple[str, ...]) -> None:
|
def remove_constraints(self, names: List[str]) -> None:
|
||||||
"""
|
"""
|
||||||
Removes the given constraints from the model.
|
Removes the given constraints from the model.
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ from pyomo.opt.base.solvers import SolverFactory
|
|||||||
|
|
||||||
from miplearn.features import VariableFeatures, ConstraintFeatures
|
from miplearn.features import VariableFeatures, ConstraintFeatures
|
||||||
from miplearn.instance.base import Instance
|
from miplearn.instance.base import Instance
|
||||||
from miplearn.solvers import _RedirectOutput
|
from miplearn.solvers import _RedirectOutput, _none_if_empty
|
||||||
from miplearn.solvers.internal import (
|
from miplearn.solvers.internal import (
|
||||||
InternalSolver,
|
InternalSolver,
|
||||||
LPSolveStats,
|
LPSolveStats,
|
||||||
@@ -113,7 +113,7 @@ class BasePyomoSolver(InternalSolver):
|
|||||||
self,
|
self,
|
||||||
cf: ConstraintFeatures,
|
cf: ConstraintFeatures,
|
||||||
tol: float = 1e-5,
|
tol: float = 1e-5,
|
||||||
) -> Tuple[bool, ...]:
|
) -> List[bool]:
|
||||||
assert cf.names is not None
|
assert cf.names is not None
|
||||||
assert cf.lhs is not None
|
assert cf.lhs is not None
|
||||||
assert cf.rhs is not None
|
assert cf.rhs is not None
|
||||||
@@ -130,7 +130,7 @@ class BasePyomoSolver(InternalSolver):
|
|||||||
result.append(lhs >= cf.rhs[i] - tol)
|
result.append(lhs >= cf.rhs[i] - tol)
|
||||||
else:
|
else:
|
||||||
result.append(abs(cf.rhs[i] - lhs) < tol)
|
result.append(abs(cf.rhs[i] - lhs) < tol)
|
||||||
return tuple(result)
|
return result
|
||||||
|
|
||||||
@overrides
|
@overrides
|
||||||
def build_test_instance_infeasible(self) -> Instance:
|
def build_test_instance_infeasible(self) -> Instance:
|
||||||
@@ -165,7 +165,7 @@ class BasePyomoSolver(InternalSolver):
|
|||||||
|
|
||||||
names: List[str] = []
|
names: List[str] = []
|
||||||
rhs: List[float] = []
|
rhs: List[float] = []
|
||||||
lhs: List[Tuple[Tuple[str, float], ...]] = []
|
lhs: List[List[Tuple[str, float]]] = []
|
||||||
senses: List[str] = []
|
senses: List[str] = []
|
||||||
dual_values: List[float] = []
|
dual_values: List[float] = []
|
||||||
slacks: List[float] = []
|
slacks: List[float] = []
|
||||||
@@ -214,7 +214,7 @@ class BasePyomoSolver(InternalSolver):
|
|||||||
raise Exception(
|
raise Exception(
|
||||||
f"Unknown expression type: {expr.__class__.__name__}"
|
f"Unknown expression type: {expr.__class__.__name__}"
|
||||||
)
|
)
|
||||||
lhs.append(tuple(lhsc))
|
lhs.append(lhsc)
|
||||||
|
|
||||||
# Extract dual values
|
# Extract dual values
|
||||||
if self._has_lp_solution:
|
if self._has_lp_solution:
|
||||||
@@ -233,24 +233,13 @@ class BasePyomoSolver(InternalSolver):
|
|||||||
names.append(constr.name)
|
names.append(constr.name)
|
||||||
_parse_constraint(constr)
|
_parse_constraint(constr)
|
||||||
|
|
||||||
rhs_t, lhs_t, senses_t = None, None, None
|
|
||||||
slacks_t, dual_values_t = None, None
|
|
||||||
if with_static:
|
|
||||||
rhs_t = tuple(rhs)
|
|
||||||
lhs_t = tuple(lhs)
|
|
||||||
senses_t = tuple(senses)
|
|
||||||
if self._has_lp_solution:
|
|
||||||
dual_values_t = tuple(dual_values)
|
|
||||||
if self._has_lp_solution or self._has_mip_solution:
|
|
||||||
slacks_t = tuple(slacks)
|
|
||||||
|
|
||||||
return ConstraintFeatures(
|
return ConstraintFeatures(
|
||||||
names=tuple(names),
|
names=_none_if_empty(names),
|
||||||
rhs=rhs_t,
|
rhs=_none_if_empty(rhs),
|
||||||
senses=senses_t,
|
senses=_none_if_empty(senses),
|
||||||
lhs=lhs_t,
|
lhs=_none_if_empty(lhs),
|
||||||
slacks=slacks_t,
|
slacks=_none_if_empty(slacks),
|
||||||
dual_values=dual_values_t,
|
dual_values=_none_if_empty(dual_values),
|
||||||
)
|
)
|
||||||
|
|
||||||
@overrides
|
@overrides
|
||||||
@@ -337,12 +326,6 @@ 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)
|
||||||
|
|
||||||
def _none_if_empty(obj: Any) -> Any:
|
|
||||||
if len(obj) == 0:
|
|
||||||
return None
|
|
||||||
else:
|
|
||||||
return obj
|
|
||||||
|
|
||||||
return VariableFeatures(
|
return VariableFeatures(
|
||||||
names=_none_if_empty(names),
|
names=_none_if_empty(names),
|
||||||
types=_none_if_empty(types),
|
types=_none_if_empty(types),
|
||||||
@@ -379,7 +362,7 @@ class BasePyomoSolver(InternalSolver):
|
|||||||
return self._termination_condition == TerminationCondition.infeasible
|
return self._termination_condition == TerminationCondition.infeasible
|
||||||
|
|
||||||
@overrides
|
@overrides
|
||||||
def remove_constraints(self, names: Tuple[str, ...]) -> None:
|
def remove_constraints(self, names: List[str]) -> None:
|
||||||
assert self.model is not None
|
assert self.model is not None
|
||||||
for name in names:
|
for name in names:
|
||||||
constr = self._cname_to_constr[name]
|
constr = self._cname_to_constr[name]
|
||||||
|
|||||||
@@ -69,18 +69,18 @@ def run_basic_usage_tests(solver: InternalSolver) -> None:
|
|||||||
assert_equals(
|
assert_equals(
|
||||||
solver.get_constraints(),
|
solver.get_constraints(),
|
||||||
ConstraintFeatures(
|
ConstraintFeatures(
|
||||||
names=("eq_capacity",),
|
names=["eq_capacity"],
|
||||||
rhs=(0.0,),
|
rhs=[0.0],
|
||||||
lhs=(
|
lhs=[
|
||||||
(
|
[
|
||||||
("x[0]", 23.0),
|
("x[0]", 23.0),
|
||||||
("x[1]", 26.0),
|
("x[1]", 26.0),
|
||||||
("x[2]", 20.0),
|
("x[2]", 20.0),
|
||||||
("x[3]", 18.0),
|
("x[3]", 18.0),
|
||||||
("z", -1.0),
|
("z", -1.0),
|
||||||
),
|
],
|
||||||
),
|
],
|
||||||
senses=("=",),
|
senses=["="],
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -120,12 +120,12 @@ def run_basic_usage_tests(solver: InternalSolver) -> None:
|
|||||||
_filter_attrs(
|
_filter_attrs(
|
||||||
solver.get_constraint_attrs(),
|
solver.get_constraint_attrs(),
|
||||||
ConstraintFeatures(
|
ConstraintFeatures(
|
||||||
basis_status=("N",),
|
basis_status=["N"],
|
||||||
dual_values=(13.538462,),
|
dual_values=[13.538462],
|
||||||
names=("eq_capacity",),
|
names=["eq_capacity"],
|
||||||
sa_rhs_down=(-24.0,),
|
sa_rhs_down=[-24.0],
|
||||||
sa_rhs_up=(2.0,),
|
sa_rhs_up=[2.0],
|
||||||
slacks=(0.0,),
|
slacks=[0.0],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -165,20 +165,20 @@ def run_basic_usage_tests(solver: InternalSolver) -> None:
|
|||||||
_filter_attrs(
|
_filter_attrs(
|
||||||
solver.get_constraint_attrs(),
|
solver.get_constraint_attrs(),
|
||||||
ConstraintFeatures(
|
ConstraintFeatures(
|
||||||
names=("eq_capacity",),
|
names=["eq_capacity"],
|
||||||
slacks=(0.0,),
|
slacks=[0.0],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
# Build new constraint and verify that it is violated
|
# Build new constraint and verify that it is violated
|
||||||
cf = ConstraintFeatures(
|
cf = ConstraintFeatures(
|
||||||
names=("cut",),
|
names=["cut"],
|
||||||
lhs=((("x[0]", 1.0),),),
|
lhs=[[("x[0]", 1.0)]],
|
||||||
rhs=(0.0,),
|
rhs=[0.0],
|
||||||
senses=("<",),
|
senses=["<"],
|
||||||
)
|
)
|
||||||
assert_equals(solver.are_constraints_satisfied(cf), (False,))
|
assert_equals(solver.are_constraints_satisfied(cf), [False])
|
||||||
|
|
||||||
# Add constraint and verify it affects solution
|
# Add constraint and verify it affects solution
|
||||||
solver.add_constraints(cf)
|
solver.add_constraints(cf)
|
||||||
@@ -187,28 +187,30 @@ def run_basic_usage_tests(solver: InternalSolver) -> None:
|
|||||||
_filter_attrs(
|
_filter_attrs(
|
||||||
solver.get_constraint_attrs(),
|
solver.get_constraint_attrs(),
|
||||||
ConstraintFeatures(
|
ConstraintFeatures(
|
||||||
names=("eq_capacity", "cut"),
|
names=["eq_capacity", "cut"],
|
||||||
rhs=(0.0, 0.0),
|
rhs=[0.0, 0.0],
|
||||||
lhs=(
|
lhs=[
|
||||||
(
|
[
|
||||||
("x[0]", 23.0),
|
("x[0]", 23.0),
|
||||||
("x[1]", 26.0),
|
("x[1]", 26.0),
|
||||||
("x[2]", 20.0),
|
("x[2]", 20.0),
|
||||||
("x[3]", 18.0),
|
("x[3]", 18.0),
|
||||||
("z", -1.0),
|
("z", -1.0),
|
||||||
),
|
],
|
||||||
(("x[0]", 1.0),),
|
[
|
||||||
),
|
("x[0]", 1.0),
|
||||||
senses=("=", "<"),
|
],
|
||||||
|
],
|
||||||
|
senses=["=", "<"],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
stats = solver.solve()
|
stats = solver.solve()
|
||||||
assert_equals(stats.mip_lower_bound, 1030.0)
|
assert_equals(stats.mip_lower_bound, 1030.0)
|
||||||
assert_equals(solver.are_constraints_satisfied(cf), (True,))
|
assert_equals(solver.are_constraints_satisfied(cf), [True])
|
||||||
|
|
||||||
# Remove the new constraint
|
# Remove the new constraint
|
||||||
solver.remove_constraints(("cut",))
|
solver.remove_constraints(["cut"])
|
||||||
|
|
||||||
# New constraint should no longer affect solution
|
# New constraint should no longer affect solution
|
||||||
stats = solver.solve()
|
stats = solver.solve()
|
||||||
|
|||||||
@@ -33,20 +33,20 @@ def sample() -> Sample:
|
|||||||
lazy_constraint_count=4,
|
lazy_constraint_count=4,
|
||||||
),
|
),
|
||||||
constraints=ConstraintFeatures(
|
constraints=ConstraintFeatures(
|
||||||
names=("c1", "c2", "c3", "c4", "c5"),
|
names=["c1", "c2", "c3", "c4", "c5"],
|
||||||
categories=(
|
categories=[
|
||||||
"type-a",
|
"type-a",
|
||||||
"type-a",
|
"type-a",
|
||||||
"type-a",
|
"type-a",
|
||||||
"type-b",
|
"type-b",
|
||||||
"type-b",
|
"type-b",
|
||||||
),
|
],
|
||||||
lazy=(True, True, True, True, False),
|
lazy=[True, True, True, True, False],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
after_lp=Features(
|
after_lp=Features(
|
||||||
instance=InstanceFeatures(),
|
instance=InstanceFeatures(),
|
||||||
constraints=ConstraintFeatures(names=("c1", "c2", "c3", "c4", "c5")),
|
constraints=ConstraintFeatures(names=["c1", "c2", "c3", "c4", "c5"]),
|
||||||
),
|
),
|
||||||
after_mip=Features(
|
after_mip=Features(
|
||||||
extra={
|
extra={
|
||||||
@@ -132,7 +132,7 @@ def test_usage_with_solver(instance: Instance) -> None:
|
|||||||
|
|
||||||
# Should ask internal solver to remove some constraints
|
# Should ask internal solver to remove some constraints
|
||||||
assert internal.remove_constraints.call_count == 1
|
assert internal.remove_constraints.call_count == 1
|
||||||
internal.remove_constraints.assert_has_calls([call(("c3",))])
|
internal.remove_constraints.assert_has_calls([call(["c3"])])
|
||||||
|
|
||||||
# LearningSolver calls after_iteration (first time)
|
# LearningSolver calls after_iteration (first time)
|
||||||
should_repeat = component.iteration_cb(solver, instance, None)
|
should_repeat = component.iteration_cb(solver, instance, None)
|
||||||
@@ -141,7 +141,7 @@ def test_usage_with_solver(instance: Instance) -> None:
|
|||||||
# Should ask internal solver to verify if constraints in the pool are
|
# Should ask internal solver to verify if constraints in the pool are
|
||||||
# satisfied and add the ones that are not
|
# satisfied and add the ones that are not
|
||||||
assert sample.after_load.constraints is not None
|
assert sample.after_load.constraints is not None
|
||||||
c = sample.after_load.constraints[False, False, True, False, False]
|
c = sample.after_load.constraints[[False, False, True, False, False]]
|
||||||
internal.are_constraints_satisfied.assert_called_once_with(c, tol=1.0)
|
internal.are_constraints_satisfied.assert_called_once_with(c, tol=1.0)
|
||||||
internal.are_constraints_satisfied.reset_mock()
|
internal.are_constraints_satisfied.reset_mock()
|
||||||
internal.add_constraints.assert_called_once_with(c)
|
internal.add_constraints.assert_called_once_with(c)
|
||||||
|
|||||||
@@ -65,26 +65,26 @@ def test_knapsack() -> None:
|
|||||||
assert_equals(
|
assert_equals(
|
||||||
_round(features.constraints),
|
_round(features.constraints),
|
||||||
ConstraintFeatures(
|
ConstraintFeatures(
|
||||||
basis_status=("N",),
|
basis_status=["N"],
|
||||||
categories=("eq_capacity",),
|
categories=["eq_capacity"],
|
||||||
dual_values=(13.538462,),
|
dual_values=[13.538462],
|
||||||
names=("eq_capacity",),
|
names=["eq_capacity"],
|
||||||
lazy=(False,),
|
lazy=[False],
|
||||||
lhs=(
|
lhs=[
|
||||||
(
|
[
|
||||||
("x[0]", 23.0),
|
("x[0]", 23.0),
|
||||||
("x[1]", 26.0),
|
("x[1]", 26.0),
|
||||||
("x[2]", 20.0),
|
("x[2]", 20.0),
|
||||||
("x[3]", 18.0),
|
("x[3]", 18.0),
|
||||||
("z", -1.0),
|
("z", -1.0),
|
||||||
),
|
],
|
||||||
),
|
],
|
||||||
rhs=(0.0,),
|
rhs=[0.0],
|
||||||
sa_rhs_down=(-24.0,),
|
sa_rhs_down=[-24.0],
|
||||||
sa_rhs_up=(2.0,),
|
sa_rhs_up=[2.0],
|
||||||
senses=("=",),
|
senses=["="],
|
||||||
slacks=(0.0,),
|
slacks=[0.0],
|
||||||
user_features=((0.0,),),
|
user_features=[[0.0]],
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
assert_equals(
|
assert_equals(
|
||||||
@@ -98,39 +98,39 @@ def test_knapsack() -> None:
|
|||||||
|
|
||||||
def test_constraint_getindex() -> None:
|
def test_constraint_getindex() -> None:
|
||||||
cf = ConstraintFeatures(
|
cf = ConstraintFeatures(
|
||||||
names=("c1", "c2", "c3"),
|
names=["c1", "c2", "c3"],
|
||||||
rhs=(1.0, 2.0, 3.0),
|
rhs=[1.0, 2.0, 3.0],
|
||||||
senses=("=", "<", ">"),
|
senses=["=", "<", ">"],
|
||||||
lhs=(
|
lhs=[
|
||||||
(
|
[
|
||||||
("x1", 1.0),
|
("x1", 1.0),
|
||||||
("x2", 1.0),
|
("x2", 1.0),
|
||||||
),
|
],
|
||||||
(
|
[
|
||||||
("x2", 2.0),
|
("x2", 2.0),
|
||||||
("x3", 2.0),
|
("x3", 2.0),
|
||||||
),
|
],
|
||||||
(
|
[
|
||||||
("x3", 3.0),
|
("x3", 3.0),
|
||||||
("x4", 3.0),
|
("x4", 3.0),
|
||||||
),
|
],
|
||||||
),
|
],
|
||||||
)
|
)
|
||||||
assert_equals(
|
assert_equals(
|
||||||
cf[True, False, True],
|
cf[[True, False, True]],
|
||||||
ConstraintFeatures(
|
ConstraintFeatures(
|
||||||
names=("c1", "c3"),
|
names=["c1", "c3"],
|
||||||
rhs=(1.0, 3.0),
|
rhs=[1.0, 3.0],
|
||||||
senses=("=", ">"),
|
senses=["=", ">"],
|
||||||
lhs=(
|
lhs=[
|
||||||
(
|
[
|
||||||
("x1", 1.0),
|
("x1", 1.0),
|
||||||
("x2", 1.0),
|
("x2", 1.0),
|
||||||
),
|
],
|
||||||
(
|
[
|
||||||
("x3", 3.0),
|
("x3", 3.0),
|
||||||
("x4", 3.0),
|
("x4", 3.0),
|
||||||
),
|
],
|
||||||
),
|
],
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user