mirror of
https://github.com/ANL-CEEESA/MIPLearn.git
synced 2025-12-06 09:28:51 -06:00
Add after_solve_lp callback; make dict keys consistent
This commit is contained in:
@@ -144,8 +144,8 @@ class GurobiSolver(InternalSolver):
|
||||
if not self.is_infeasible():
|
||||
opt_value = self.model.objVal
|
||||
return {
|
||||
"Optimal value": opt_value,
|
||||
"Log": log,
|
||||
"LP value": opt_value,
|
||||
"LP log": log,
|
||||
}
|
||||
|
||||
def solve(
|
||||
@@ -205,9 +205,8 @@ class GurobiSolver(InternalSolver):
|
||||
"Wallclock time": total_wallclock_time,
|
||||
"Nodes": total_nodes,
|
||||
"Sense": sense,
|
||||
"Log": log,
|
||||
"MIP log": log,
|
||||
"Warm start value": ws_value,
|
||||
"LP value": None,
|
||||
}
|
||||
return stats
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ from miplearn.instance import Instance
|
||||
from miplearn.solvers import _RedirectOutput
|
||||
from miplearn.solvers.internal import InternalSolver
|
||||
from miplearn.solvers.pyomo.gurobi import GurobiPyomoSolver
|
||||
from miplearn.types import TrainingSample, LearningSolveStats
|
||||
from miplearn.types import TrainingSample, LearningSolveStats, MIPSolveStats
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -85,8 +85,8 @@ class LearningSolver:
|
||||
use_lazy_cb: bool
|
||||
If true, use native solver callbacks for enforcing lazy constraints,
|
||||
instead of a simple loop. May not be supported by all solvers.
|
||||
solve_lp_first: bool
|
||||
If true, solve LP relaxation first, then solve original MIP. This
|
||||
solve_lp: bool
|
||||
If true, solve the root LP relaxation before solving the MIP. This
|
||||
option should be activated if the LP relaxation is not very
|
||||
expensive to solve and if it provides good hints for the integer
|
||||
solution.
|
||||
@@ -103,7 +103,7 @@ class LearningSolver:
|
||||
mode: str = "exact",
|
||||
solver: Callable[[], InternalSolver] = None,
|
||||
use_lazy_cb: bool = False,
|
||||
solve_lp_first: bool = True,
|
||||
solve_lp: bool = True,
|
||||
simulate_perfect: bool = False,
|
||||
):
|
||||
if solver is None:
|
||||
@@ -113,7 +113,7 @@ class LearningSolver:
|
||||
self.internal_solver: Optional[InternalSolver] = None
|
||||
self.mode: str = mode
|
||||
self.simulate_perfect: bool = simulate_perfect
|
||||
self.solve_lp_first: bool = solve_lp_first
|
||||
self.solve_lp: bool = solve_lp
|
||||
self.solver_factory: Callable[[], InternalSolver] = solver
|
||||
self.tee = False
|
||||
self.use_lazy_cb: bool = use_lazy_cb
|
||||
@@ -164,6 +164,9 @@ class LearningSolver:
|
||||
instance.training_data = []
|
||||
instance.training_data += [training_sample]
|
||||
|
||||
# Initialize stats
|
||||
stats: LearningSolveStats = {}
|
||||
|
||||
# Initialize internal solver
|
||||
self.tee = tee
|
||||
self.internal_solver = self.solver_factory()
|
||||
@@ -175,22 +178,26 @@ class LearningSolver:
|
||||
extractor = ModelFeaturesExtractor(self.internal_solver)
|
||||
instance.model_features = extractor.extract()
|
||||
|
||||
# Solve linear relaxation
|
||||
if self.solve_lp_first:
|
||||
logger.info("Solving LP relaxation...")
|
||||
# Solve root LP relaxation
|
||||
if self.solve_lp:
|
||||
logger.debug("Running before_solve_lp callbacks...")
|
||||
for component in self.components.values():
|
||||
component.before_solve_lp(self, instance, model)
|
||||
|
||||
logger.info("Solving root LP relaxation...")
|
||||
lp_stats = self.internal_solver.solve_lp(tee=tee)
|
||||
stats.update(cast(LearningSolveStats, lp_stats))
|
||||
training_sample["LP solution"] = self.internal_solver.get_solution()
|
||||
training_sample["LP value"] = lp_stats["Optimal value"]
|
||||
training_sample["LP log"] = lp_stats["Log"]
|
||||
training_sample["LP value"] = lp_stats["LP value"]
|
||||
training_sample["LP log"] = lp_stats["LP log"]
|
||||
|
||||
logger.debug("Running after_solve_lp callbacks...")
|
||||
for component in self.components.values():
|
||||
component.after_solve_lp(self, instance, model, stats, training_sample)
|
||||
else:
|
||||
training_sample["LP solution"] = self.internal_solver.get_empty_solution()
|
||||
training_sample["LP value"] = 0.0
|
||||
|
||||
# Before-solve callbacks
|
||||
logger.debug("Running before_solve_mip callbacks...")
|
||||
for component in self.components.values():
|
||||
component.before_solve_mip(self, instance, model)
|
||||
|
||||
# Define wrappers
|
||||
def iteration_cb_wrapper() -> bool:
|
||||
should_repeat = False
|
||||
@@ -212,16 +219,19 @@ class LearningSolver:
|
||||
if self.use_lazy_cb:
|
||||
lazy_cb = lazy_cb_wrapper
|
||||
|
||||
# Before-solve callbacks
|
||||
logger.debug("Running before_solve_mip callbacks...")
|
||||
for component in self.components.values():
|
||||
component.before_solve_mip(self, instance, model)
|
||||
|
||||
# Solve MIP
|
||||
logger.info("Solving MIP...")
|
||||
stats = cast(
|
||||
LearningSolveStats,
|
||||
self.internal_solver.solve(
|
||||
tee=tee,
|
||||
iteration_cb=iteration_cb_wrapper,
|
||||
lazy_cb=lazy_cb,
|
||||
),
|
||||
mip_stats = self.internal_solver.solve(
|
||||
tee=tee,
|
||||
iteration_cb=iteration_cb_wrapper,
|
||||
lazy_cb=lazy_cb,
|
||||
)
|
||||
stats.update(cast(LearningSolveStats, mip_stats))
|
||||
if "LP value" in training_sample.keys():
|
||||
stats["LP value"] = training_sample["LP value"]
|
||||
stats["Solver"] = "default"
|
||||
@@ -234,7 +244,7 @@ class LearningSolver:
|
||||
# Add some information to training_sample
|
||||
training_sample["Lower bound"] = stats["Lower bound"]
|
||||
training_sample["Upper bound"] = stats["Upper bound"]
|
||||
training_sample["MIP log"] = stats["Log"]
|
||||
training_sample["MIP log"] = stats["MIP log"]
|
||||
training_sample["Solution"] = self.internal_solver.get_solution()
|
||||
|
||||
# After-solve callbacks
|
||||
|
||||
@@ -67,8 +67,8 @@ class BasePyomoSolver(InternalSolver):
|
||||
if not self.is_infeasible():
|
||||
opt_value = results["Problem"][0]["Lower bound"]
|
||||
return {
|
||||
"Optimal value": opt_value,
|
||||
"Log": streams[0].getvalue(),
|
||||
"LP value": opt_value,
|
||||
"LP log": streams[0].getvalue(),
|
||||
}
|
||||
|
||||
def _restore_integrality(self) -> None:
|
||||
@@ -114,10 +114,9 @@ class BasePyomoSolver(InternalSolver):
|
||||
"Upper bound": ub,
|
||||
"Wallclock time": total_wallclock_time,
|
||||
"Sense": self._obj_sense,
|
||||
"Log": log,
|
||||
"MIP log": log,
|
||||
"Nodes": node_count,
|
||||
"Warm start value": ws_value,
|
||||
"LP value": None,
|
||||
}
|
||||
return stats
|
||||
|
||||
|
||||
Reference in New Issue
Block a user