diff --git a/miplearn/collectors/basic.py b/miplearn/collectors/basic.py index d3d7f77..0bec085 100644 --- a/miplearn/collectors/basic.py +++ b/miplearn/collectors/basic.py @@ -58,11 +58,11 @@ class BasicCollector: # Add lazy constraints to model if ( - hasattr(model, "fix_violations") - and model.fix_violations is not None + hasattr(model, "lazy_enforce") + and model.lazy_enforce is not None ): - model.fix_violations(model, model.violations_, "aot") - h5.put_scalar("mip_constr_violations", repr(model.violations_)) + model.lazy_enforce(model, model.lazy_constrs_, "aot") + h5.put_scalar("mip_lazy", repr(model.lazy_constrs_)) # Save MPS file model.write(mps_filename) diff --git a/miplearn/components/lazy/mem.py b/miplearn/components/lazy/mem.py index 7f25b4d..eb3fc63 100644 --- a/miplearn/components/lazy/mem.py +++ b/miplearn/components/lazy/mem.py @@ -24,30 +24,30 @@ class MemorizingLazyConstrComponent: def __init__(self, clf: Any, extractor: FeaturesExtractor) -> None: self.clf = clf self.extractor = extractor - self.violations_: List[Hashable] = [] + self.constrs_: List[Hashable] = [] self.n_features_: int = 0 self.n_targets_: int = 0 def fit(self, train_h5: List[str]) -> None: logger.info("Reading training data...") n_samples = len(train_h5) - x, y, violations, n_features = [], [], [], None - violation_to_idx: Dict[Hashable, int] = {} + x, y, constrs, n_features = [], [], [], None + constr_to_idx: Dict[Hashable, int] = {} for h5_filename in train_h5: with H5File(h5_filename, "r") as h5: # Store lazy constraints - sample_violations_str = h5.get_scalar("mip_constr_violations") - assert sample_violations_str is not None - assert isinstance(sample_violations_str, str) - sample_violations = eval(sample_violations_str) - assert isinstance(sample_violations, list) + sample_constrs_str = h5.get_scalar("mip_lazy") + assert sample_constrs_str is not None + assert isinstance(sample_constrs_str, str) + sample_constrs = eval(sample_constrs_str) + assert isinstance(sample_constrs, list) y_sample = [] - for v in sample_violations: - if v not in violation_to_idx: - violation_to_idx[v] = len(violation_to_idx) - violations.append(v) - y_sample.append(violation_to_idx[v]) + for c in sample_constrs: + if c not in constr_to_idx: + constr_to_idx[c] = len(constr_to_idx) + constrs.append(c) + y_sample.append(constr_to_idx[c]) y.append(y_sample) # Extract features @@ -62,8 +62,8 @@ class MemorizingLazyConstrComponent: logger.info("Constructing matrices...") assert n_features is not None self.n_features_ = n_features - self.violations_ = violations - self.n_targets_ = len(violation_to_idx) + self.constrs_ = constrs + self.n_targets_ = len(constr_to_idx) x_np = np.vstack(x) assert x_np.shape == (n_samples, n_features) y_np = MultiLabelBinarizer().fit_transform(y) @@ -82,8 +82,8 @@ class MemorizingLazyConstrComponent: model: GurobiModel, stats: Dict[str, Any], ) -> None: - assert self.violations_ is not None - if model.fix_violations is None: + assert self.constrs_ is not None + if model.lazy_enforce is None: return # Read features @@ -99,7 +99,7 @@ class MemorizingLazyConstrComponent: y = y.reshape(-1) # Enforce constraints - violations = [self.violations_[i] for (i, yi) in enumerate(y) if yi > 0.5] + violations = [self.constrs_[i] for (i, yi) in enumerate(y) if yi > 0.5] logger.info(f"Enforcing {len(violations)} constraints ahead-of-time...") - model.fix_violations(model, violations, "aot") + model.lazy_enforce(model, violations, "aot") stats["Lazy Constraints: AOT"] = len(violations) diff --git a/miplearn/problems/tsp.py b/miplearn/problems/tsp.py index a23b9bc..c11b057 100644 --- a/miplearn/problems/tsp.py +++ b/miplearn/problems/tsp.py @@ -142,7 +142,7 @@ def build_tsp_model(data: Union[str, TravelingSalesmanData]) -> GurobiModel: name="eq_degree", ) - def find_violations(model: GurobiModel) -> List[Any]: + def lazy_separate(model: GurobiModel) -> List[Any]: violations = [] x = model.inner.cbGetSolution(model.inner._x) selected_edges = [e for e in model.inner._edges if x[e] > 0.5] @@ -159,7 +159,7 @@ def build_tsp_model(data: Union[str, TravelingSalesmanData]) -> GurobiModel: violations.append(cut_edges) return violations - def fix_violations(model: GurobiModel, violations: List[Any], where: str) -> None: + def lazy_enforce(model: GurobiModel, violations: List[Any], where: str) -> None: for violation in violations: constr = quicksum(model.inner._x[e[0], e[1]] for e in violation) >= 2 if where == "cb": @@ -172,6 +172,6 @@ def build_tsp_model(data: Union[str, TravelingSalesmanData]) -> GurobiModel: return GurobiModel( model, - find_violations=find_violations, - fix_violations=fix_violations, + lazy_separate=lazy_separate, + lazy_enforce=lazy_enforce, ) diff --git a/miplearn/solvers/gurobi.py b/miplearn/solvers/gurobi.py index 9d7cf5c..90e1a20 100644 --- a/miplearn/solvers/gurobi.py +++ b/miplearn/solvers/gurobi.py @@ -21,13 +21,13 @@ class GurobiModel(AbstractModel): def __init__( self, inner: gp.Model, - find_violations: Optional[Callable] = None, - fix_violations: Optional[Callable] = None, + lazy_separate: Optional[Callable] = None, + lazy_enforce: Optional[Callable] = None, ) -> None: - self.fix_violations = fix_violations - self.find_violations = find_violations + self.lazy_separate = lazy_separate + self.lazy_enforce = lazy_enforce self.inner = inner - self.violations_: Optional[List[Any]] = None + self.lazy_constrs_: Optional[List[Any]] = None def add_constrs( self, @@ -125,18 +125,18 @@ class GurobiModel(AbstractModel): stats["Fixed variables"] = n_fixed def optimize(self) -> None: - self.violations_ = [] + self.lazy_constrs_ = [] def callback(m: gp.Model, where: int) -> None: - assert self.find_violations is not None - assert self.violations_ is not None - assert self.fix_violations is not None + assert self.lazy_separate is not None + assert self.lazy_constrs_ is not None + assert self.lazy_enforce is not None if where == GRB.Callback.MIPSOL: - violations = self.find_violations(self) - self.violations_.extend(violations) - self.fix_violations(self, violations, "cb") + violations = self.lazy_separate(self) + self.lazy_constrs_.extend(violations) + self.lazy_enforce(self, violations, "cb") - if self.fix_violations is not None: + if self.lazy_enforce is not None: self.inner.Params.lazyConstraints = 1 self.inner.optimize(callback) else: diff --git a/tests/components/lazy/test_mem.py b/tests/components/lazy/test_mem.py index 9f7618c..54147a4 100644 --- a/tests/components/lazy/test_mem.py +++ b/tests/components/lazy/test_mem.py @@ -33,10 +33,10 @@ def test_mem_component( ] # Should store violations - assert comp.violations_ is not None + assert comp.constrs_ is not None assert comp.n_features_ == 190 assert comp.n_targets_ == 22 - assert len(comp.violations_) == 22 + assert len(comp.constrs_) == 22 # Call before-mip stats: Dict[str, Any] = {} diff --git a/tests/fixtures/tsp-n20-00000.h5 b/tests/fixtures/tsp-n20-00000.h5 index 57d4990..d6bf57a 100644 Binary files a/tests/fixtures/tsp-n20-00000.h5 and b/tests/fixtures/tsp-n20-00000.h5 differ diff --git a/tests/fixtures/tsp-n20-00000.mps.gz b/tests/fixtures/tsp-n20-00000.mps.gz index a0debe5..4699bf6 100644 Binary files a/tests/fixtures/tsp-n20-00000.mps.gz and b/tests/fixtures/tsp-n20-00000.mps.gz differ diff --git a/tests/fixtures/tsp-n20-00000.pkl.gz b/tests/fixtures/tsp-n20-00000.pkl.gz index d0273ff..f0baeca 100644 Binary files a/tests/fixtures/tsp-n20-00000.pkl.gz and b/tests/fixtures/tsp-n20-00000.pkl.gz differ diff --git a/tests/fixtures/tsp-n20-00001.h5 b/tests/fixtures/tsp-n20-00001.h5 index 469a6ce..f54f99e 100644 Binary files a/tests/fixtures/tsp-n20-00001.h5 and b/tests/fixtures/tsp-n20-00001.h5 differ diff --git a/tests/fixtures/tsp-n20-00001.mps.gz b/tests/fixtures/tsp-n20-00001.mps.gz index 2845736..907d7cd 100644 Binary files a/tests/fixtures/tsp-n20-00001.mps.gz and b/tests/fixtures/tsp-n20-00001.mps.gz differ diff --git a/tests/fixtures/tsp-n20-00001.pkl.gz b/tests/fixtures/tsp-n20-00001.pkl.gz index 115bef8..9286666 100644 Binary files a/tests/fixtures/tsp-n20-00001.pkl.gz and b/tests/fixtures/tsp-n20-00001.pkl.gz differ diff --git a/tests/fixtures/tsp-n20-00002.h5 b/tests/fixtures/tsp-n20-00002.h5 index 7a24762..7f209f2 100644 Binary files a/tests/fixtures/tsp-n20-00002.h5 and b/tests/fixtures/tsp-n20-00002.h5 differ diff --git a/tests/fixtures/tsp-n20-00002.mps.gz b/tests/fixtures/tsp-n20-00002.mps.gz index de15dd3..6fb3acb 100644 Binary files a/tests/fixtures/tsp-n20-00002.mps.gz and b/tests/fixtures/tsp-n20-00002.mps.gz differ diff --git a/tests/fixtures/tsp-n20-00002.pkl.gz b/tests/fixtures/tsp-n20-00002.pkl.gz index 702ebdc..43dc6a7 100644 Binary files a/tests/fixtures/tsp-n20-00002.pkl.gz and b/tests/fixtures/tsp-n20-00002.pkl.gz differ