From bd13518d8855d6c6d09c067017524e92ca9611ff Mon Sep 17 00:00:00 2001 From: Sukrit Kalra Date: Fri, 1 Dec 2023 06:21:03 -0800 Subject: [PATCH] Add ability to handle infeasible models when optimizations are turned off. --- .../tetrisched/include/tetrisched/Solver.hpp | 6 ++++ schedulers/tetrisched/python/Backends.cpp | 2 ++ schedulers/tetrisched/src/GurobiSolver.cpp | 8 +++--- schedulers/tetrisched/src/Scheduler.cpp | 4 ++- schedulers/tetrisched_scheduler.py | 28 +++++++++++++++---- 5 files changed, 37 insertions(+), 11 deletions(-) diff --git a/schedulers/tetrisched/include/tetrisched/Solver.hpp b/schedulers/tetrisched/include/tetrisched/Solver.hpp index 05500d08..101ce179 100644 --- a/schedulers/tetrisched/include/tetrisched/Solver.hpp +++ b/schedulers/tetrisched/include/tetrisched/Solver.hpp @@ -47,6 +47,12 @@ struct SolverSolution { /// The time taken by the solver to find the solution (in microseconds). uint64_t solverTimeMicroseconds; + /// Check if the solution was valid. + bool isValid() const { + return solutionType == SolutionType::FEASIBLE || + solutionType == SolutionType::OPTIMAL; + } + /// Get a string representation of the Solver's type. std::string getSolutionTypeStr() const { switch (solutionType) { diff --git a/schedulers/tetrisched/python/Backends.cpp b/schedulers/tetrisched/python/Backends.cpp index 558dda32..37835034 100644 --- a/schedulers/tetrisched/python/Backends.cpp +++ b/schedulers/tetrisched/python/Backends.cpp @@ -41,6 +41,8 @@ void defineSolverSolution(py::module_& tetrisched_m) { [](const tetrisched::SolverSolution& solution) { return solution.solverTimeMicroseconds; }) + .def("isValid", &tetrisched::SolverSolution::isValid, + "Check if the solution was valid.") .def("__str__", [](const tetrisched::SolverSolution& solution) { return "SolverSolution -#include #include +#include namespace tetrisched { GurobiSolver::GurobiSolver() @@ -241,16 +241,16 @@ SolverSolutionPtr GurobiSolver::solveModel() { break; case GRB_INFEASIBLE: solverSolution->solutionType = SolutionType::INFEASIBLE; - break; + return solverSolution; case GRB_INF_OR_UNBD: case GRB_UNBOUNDED: solverSolution->solutionType = SolutionType::UNBOUNDED; - break; + return solverSolution; default: solverSolution->solutionType = SolutionType::UNKNOWN; TETRISCHED_DEBUG("The Gurobi solver returned the value: " << gurobiModel->get(GRB_IntAttr_Status)); - break; + return solverSolution; } TETRISCHED_DEBUG("The Gurobi solver took " << solverSolution->solverTimeMicroseconds << " microseconds " diff --git a/schedulers/tetrisched/src/Scheduler.cpp b/schedulers/tetrisched/src/Scheduler.cpp index 09f09e7f..3b11c74e 100644 --- a/schedulers/tetrisched/src/Scheduler.cpp +++ b/schedulers/tetrisched/src/Scheduler.cpp @@ -116,7 +116,9 @@ void Scheduler::schedule(Time currentTime) { solverSolution = this->solver->solveModel(); // Populate the results from the solver into the expression tree. - this->expression.value()->populateResults(solverModel); + if (solverSolution.has_value() && solverSolution.value()->isValid()) { + this->expression.value()->populateResults(solverModel); + } } SolverSolutionPtr Scheduler::getLastSolverSolution() const { diff --git a/schedulers/tetrisched_scheduler.py b/schedulers/tetrisched_scheduler.py index ba6662a6..624e6eeb 100644 --- a/schedulers/tetrisched_scheduler.py +++ b/schedulers/tetrisched_scheduler.py @@ -337,6 +337,21 @@ def schedule( ) raise e + # If the solver could not solve the model, log an error. + if not self._scheduler.getLastSolverSolution().isValid(): + self._logger.error( + f"[{sim_time.time}] The solver failed to find a solution for " + f"the STRL expression. Dumping the model to " + f"tetrisched_error_{sim_time.time}.lp and STRL expression to " + f"tetrisched_error_{sim_time.time}.dot." + ) + objective_strl.exportToDot( + os.path.join(self._log_dir, f"tetrisched_error_{sim_time.time}.dot") + ) + self._scheduler.exportLastSolverModel( + os.path.join(self._log_dir, f"tetrisched_error_{sim_time.time}.lp") + ) + # If requested, log the model to a file. if self._log_to_file or sim_time.time in self._log_times: self._scheduler.exportLastSolverModel( @@ -353,12 +368,13 @@ def schedule( # Retrieve the solution and check if we were able to schedule anything. solverSolution = objective_strl.getSolution() - self._logger.info( - f"[{sim_time.time}] Solver returned utility of {solverSolution.utility}" - f" and took {solver_time} to solve. The solution result " - f"was {self._scheduler.getLastSolverSolution()}." - ) - if solverSolution.utility > 0: + if solverSolution is not None and solverSolution.utility > 0: + self._logger.info( + f"[{sim_time.time}] Solver returned utility of " + f"{solverSolution.utility} and took {solver_time} to solve. The " + f"solution result was {self._scheduler.getLastSolverSolution()}." + ) + # Retrieve the Placements for each task. for task in tasks_to_be_scheduled: task_placement = solverSolution.getPlacement(task.unique_name)