mirror of
https://github.com/ANL-CEEESA/MIPLearn.git
synced 2025-12-06 01:18:52 -06:00
Replace build_lazy_constraint by enforce_lazy_constraint
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
|
||||
import logging
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Any, List, Optional, Hashable
|
||||
from typing import Any, List, Optional, Hashable, TYPE_CHECKING
|
||||
|
||||
from overrides import EnforceOverrides
|
||||
|
||||
@@ -13,9 +13,11 @@ from miplearn.types import VariableName, Category
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from miplearn.solvers.learning import InternalSolver
|
||||
|
||||
# noinspection PyMethodMayBeStatic
|
||||
class Instance(ABC):
|
||||
class Instance(ABC, EnforceOverrides):
|
||||
"""
|
||||
Abstract class holding all the data necessary to generate a concrete model of the
|
||||
proble.
|
||||
@@ -109,28 +111,40 @@ class Instance(ABC):
|
||||
def is_constraint_lazy(self, cid: str) -> bool:
|
||||
return False
|
||||
|
||||
def find_violated_lazy_constraints(self, model: Any) -> List[Hashable]:
|
||||
def find_violated_lazy_constraints(
|
||||
self,
|
||||
solver: "InternalSolver",
|
||||
model: Any,
|
||||
) -> List[Hashable]:
|
||||
"""
|
||||
Returns lazy constraint violations found for the current solution.
|
||||
|
||||
After solving a model, LearningSolver will ask the instance to identify which
|
||||
lazy constraints are violated by the current solution. For each identified
|
||||
violation, LearningSolver will then call the build_lazy_constraint, add the
|
||||
generated Pyomo constraint to the model, then resolve the problem. The
|
||||
process repeats until no further lazy constraint violations are found.
|
||||
violation, LearningSolver will then call the enforce_lazy_constraint and
|
||||
resolve the problem. The process repeats until no further lazy constraint
|
||||
violations are found.
|
||||
|
||||
Each "violation" is simply a string, a tuple or any other hashable type which
|
||||
allows the instance to identify unambiguously which lazy constraint should be
|
||||
generated. In the Traveling Salesman Problem, for example, a subtour
|
||||
violation could be a frozen set containing the cities in the subtour.
|
||||
|
||||
The current solution can be queried with `solver.get_solution()`. If the solver
|
||||
is configured to use lazy callbacks, this solution may be non-integer.
|
||||
|
||||
For a concrete example, see TravelingSalesmanInstance.
|
||||
"""
|
||||
return []
|
||||
|
||||
def build_lazy_constraint(self, model: Any, violation: Hashable) -> Any:
|
||||
def enforce_lazy_constraint(
|
||||
self,
|
||||
solver: "InternalSolver",
|
||||
model: Any,
|
||||
violation: Hashable,
|
||||
) -> None:
|
||||
"""
|
||||
Returns a Pyomo constraint which fixes a given violation.
|
||||
Adds constraints to the model to ensure that the given violation is fixed.
|
||||
|
||||
This method is typically called immediately after
|
||||
find_violated_lazy_constraints. The violation object provided to this method
|
||||
@@ -138,11 +152,13 @@ class Instance(ABC):
|
||||
find_violated_lazy_constraints. After some training, LearningSolver may
|
||||
decide to proactively build some lazy constraints at the beginning of the
|
||||
optimization process, before a solution is even available. In this case,
|
||||
build_lazy_constraints will be called without a corresponding call to
|
||||
enforce_lazy_constraints will be called without a corresponding call to
|
||||
find_violated_lazy_constraints.
|
||||
|
||||
The implementation should not directly add the constraint to the model. The
|
||||
constraint will be added by LearningSolver after the method returns.
|
||||
Note that this method can be called either before the optimization starts or
|
||||
from within a callback. To ensure that constraints are added correctly in
|
||||
either case, it is recommended to use `solver.add_constraint`, instead of
|
||||
modifying the `model` object directly.
|
||||
|
||||
For a concrete example, see TravelingSalesmanInstance.
|
||||
"""
|
||||
|
||||
@@ -6,13 +6,16 @@ import gc
|
||||
import gzip
|
||||
import os
|
||||
import pickle
|
||||
from typing import Optional, Any, List, Hashable, cast, IO
|
||||
from typing import Optional, Any, List, Hashable, cast, IO, TYPE_CHECKING
|
||||
|
||||
from overrides import overrides
|
||||
|
||||
from miplearn.instance.base import logger, Instance
|
||||
from miplearn.types import VariableName, Category
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from miplearn.solvers.learning import InternalSolver
|
||||
|
||||
|
||||
class PickleGzInstance(Instance):
|
||||
"""
|
||||
@@ -79,14 +82,23 @@ class PickleGzInstance(Instance):
|
||||
return self.instance.is_constraint_lazy(cid)
|
||||
|
||||
@overrides
|
||||
def find_violated_lazy_constraints(self, model: Any) -> List[Hashable]:
|
||||
def find_violated_lazy_constraints(
|
||||
self,
|
||||
solver: "InternalSolver",
|
||||
model: Any,
|
||||
) -> List[Hashable]:
|
||||
assert self.instance is not None
|
||||
return self.instance.find_violated_lazy_constraints(model)
|
||||
return self.instance.find_violated_lazy_constraints(solver, model)
|
||||
|
||||
@overrides
|
||||
def build_lazy_constraint(self, model: Any, violation: Hashable) -> Any:
|
||||
def enforce_lazy_constraint(
|
||||
self,
|
||||
solver: "InternalSolver",
|
||||
model: Any,
|
||||
violation: Hashable,
|
||||
) -> None:
|
||||
assert self.instance is not None
|
||||
return self.instance.build_lazy_constraint(model, violation)
|
||||
self.instance.enforce_lazy_constraint(solver, model, violation)
|
||||
|
||||
@overrides
|
||||
def find_violated_user_cuts(self, model: Any) -> List[Hashable]:
|
||||
@@ -94,9 +106,9 @@ class PickleGzInstance(Instance):
|
||||
return self.instance.find_violated_user_cuts(model)
|
||||
|
||||
@overrides
|
||||
def build_user_cut(self, model: Any, violation: Hashable) -> Any:
|
||||
def build_user_cut(self, model: Any, violation: Hashable) -> None:
|
||||
assert self.instance is not None
|
||||
return self.instance.build_user_cut(model, violation)
|
||||
self.instance.build_user_cut(model, violation)
|
||||
|
||||
@overrides
|
||||
def load(self) -> None:
|
||||
|
||||
Reference in New Issue
Block a user