Skip to content

Commit

Permalink
Interrupt Gurobi if it's stuck without new incumbents.
Browse files Browse the repository at this point in the history
  • Loading branch information
sukritkalra committed Dec 31, 2023
1 parent 66e6cd4 commit c0fb123
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 14 deletions.
4 changes: 4 additions & 0 deletions schedulers/tetrisched/include/tetrisched/GurobiSolver.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ class GurobiSolver : public Solver {
struct GurobiInterruptParams {
/// The time limit for the optimization (in milliseconds).
std::optional<Time> timeLimitMs;
/// The time limit until a new solution is found (in milliseconds).
std::optional<Time> newSolutionTimeLimitMs;
/// The upper bound of the utility (if available).
std::optional<TETRISCHED_ILP_TYPE> utilityUpperBound;
};
Expand All @@ -24,6 +26,8 @@ class GurobiSolver : public Solver {
GurobiInterruptParams params;
/// The start time of the optimization.
std::chrono::steady_clock::time_point startTime;
/// The time at which the last incumbent solution was found.
std::chrono::steady_clock::time_point lastIncumbentSolutionTime;

public:
/// Create a new GurobiInterruptOptimizationCallback.
Expand Down
53 changes: 39 additions & 14 deletions schedulers/tetrisched/src/GurobiSolver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,26 +13,43 @@ namespace tetrisched {
*/
GurobiSolver::GurobiInterruptOptimizationCallback::
GurobiInterruptOptimizationCallback(GurobiInterruptParams params)
: params(params), startTime(std::chrono::steady_clock::now()) {}
: params(params) {
auto currentTime = std::chrono::steady_clock::now();
startTime = currentTime;
lastIncumbentSolutionTime = currentTime;
}

void GurobiSolver::GurobiInterruptOptimizationCallback::callback() {
try {
auto currentTime = std::chrono::steady_clock::now();
if (where == GRB_CB_POLLING && params.timeLimitMs.has_value()) {
auto elapsedTimeMs =
std::chrono::duration_cast<std::chrono::milliseconds>(currentTime -
startTime)
.count();

if (elapsedTimeMs > params.timeLimitMs.value()) {
abort();
if (where == GRB_CB_POLLING) {
if (params.timeLimitMs.has_value()) {
auto elapsedTimeMs =
std::chrono::duration_cast<std::chrono::milliseconds>(currentTime -
startTime)
.count();

if (elapsedTimeMs > params.timeLimitMs.value()) {
abort();
}
}
if (params.newSolutionTimeLimitMs.has_value()) {
auto elapsedTimeMs =
std::chrono::duration_cast<std::chrono::milliseconds>(
currentTime - lastIncumbentSolutionTime)
.count();

if (elapsedTimeMs > params.newSolutionTimeLimitMs.value()) {
abort();
}
}
} else if (where == GRB_CB_MIPSOL && params.utilityUpperBound.has_value()) {
if (getDoubleInfo(GRB_CB_MIPSOL_OBJ) >=
params.utilityUpperBound.value() -
TETRISCHED_SOLUTION_UPPER_BOUND_DELTA) {
auto solutionObjectiveValue = getDoubleInfo(GRB_CB_MIPSOL_OBJ);
if (solutionObjectiveValue >= params.utilityUpperBound.value() -
TETRISCHED_SOLUTION_UPPER_BOUND_DELTA) {
abort();
}
lastIncumbentSolutionTime = currentTime;
}
} catch (GRBException& e) {
std::cout << "Gurobi Solver failed with error code: " << e.getErrorCode()
Expand Down Expand Up @@ -80,6 +97,9 @@ void GurobiSolver::setParameters(GRBModel& gurobiModel) {
// Ask Gurobi to find new incumbent solutions rather than prove bounds.
gurobiModel.set(GRB_IntParam_MIPFocus, 1);

// Ask Gurobi to solve the MIP concurrently with different paths.
// gurobiModel.set(GRB_IntParam_ConcurrentMIP, 8);

// Ask Gurobi to not output to the console, and instead direct it
// to the specified file.
// gurobiModel.set(GRB_IntParam_LogToConsole, 0);
Expand Down Expand Up @@ -197,8 +217,9 @@ GRBConstr GurobiSolver::translateConstraint(
GRBLinExpr GurobiSolver::translateObjectiveFunction(
GRBModel& /* gurobiModel */,
const ObjectiveFunctionPtr& objectiveFunction) const {
// TODO (Sukrit): We are currently assuming that all constraints and objective
// functions are linear. We may need to support quadratic constraints.
// TODO (Sukrit): We are currently assuming that all constraints and
// objective functions are linear. We may need to support quadratic
// constraints.
GRBLinExpr objectiveExpr;

// Construct all the terms.
Expand Down Expand Up @@ -313,6 +334,10 @@ SolverSolutionPtr GurobiSolver::solveModel() {
solverSolution->numNonZeroCoefficients = gurobiModel->get(GRB_IntAttr_NumNZs);

// Construct the Interrupt callback, and register it with the model.
// TODO (Sukrit): This should be configurable, but for now, we just interrupt
// after 5 minutes.
constexpr auto interruptTimeLimitMs = 5 * 60 * 1000;
interruptParams.newSolutionTimeLimitMs = interruptTimeLimitMs;
GurobiInterruptOptimizationCallback interruptCallback(interruptParams);
gurobiModel->setCallback(&interruptCallback);

Expand Down

0 comments on commit c0fb123

Please sign in to comment.