From c00c0dbaad7c34be7f6f29e1033e2c294d60da74 Mon Sep 17 00:00:00 2001 From: Sukrit Kalra Date: Tue, 26 Sep 2023 17:43:12 -0700 Subject: [PATCH] Implement solve() method for CPLEX. --- schedulers/tetrisched/CMakeLists.txt | 2 +- .../include/tetrisched/CPLEXSolver.hpp | 6 ++++ .../include/tetrisched/GoogleCPSolver.hpp | 3 ++ .../include/tetrisched/GurobiSolver.hpp | 4 ++- .../tetrisched/include/tetrisched/Solver.hpp | 3 ++ .../include/tetrisched/SolverModel.hpp | 8 +++++ schedulers/tetrisched/src/CPLEXSolver.cpp | 32 +++++++++++++++++++ schedulers/tetrisched/src/GoogleCPSolver.cpp | 4 +++ schedulers/tetrisched/src/GurobiSolver.cpp | 5 ++- schedulers/tetrisched/src/SolverModel.cpp | 5 +++ schedulers/tetrisched/test/test_solver.cpp | 18 ++++++++--- 11 files changed, 83 insertions(+), 7 deletions(-) diff --git a/schedulers/tetrisched/CMakeLists.txt b/schedulers/tetrisched/CMakeLists.txt index 96366cbe..b1abe4a1 100644 --- a/schedulers/tetrisched/CMakeLists.txt +++ b/schedulers/tetrisched/CMakeLists.txt @@ -67,7 +67,7 @@ endif() if (EXISTS "${ORTOOLS_DIR}") message("-- Adding Google OR-Tools : ${ORTOOLS_DIR}") - add_compile_definitions(_TETRISCHED_WITH_ORTOOLS_) + add_compile_definitions(_TETRISCHED_WITH_OR_TOOLS_) set(ORTOOLS_LINK_LIBRARIES "ortools") diff --git a/schedulers/tetrisched/include/tetrisched/CPLEXSolver.hpp b/schedulers/tetrisched/include/tetrisched/CPLEXSolver.hpp index c513b35f..da72de32 100644 --- a/schedulers/tetrisched/include/tetrisched/CPLEXSolver.hpp +++ b/schedulers/tetrisched/include/tetrisched/CPLEXSolver.hpp @@ -46,6 +46,12 @@ class CPLEXSolver : public Solver { /// Export the constructed model to the given file. void exportModel(const std::string& fileName) override; + + /// Solve the constructed model. + void solveModel() override; + + /// Destroy the CPLEXSolver. + ~CPLEXSolver(); }; } // namespace tetrisched #endif // _TETRISCHED_CPLEX_SOLVER_HPP_ diff --git a/schedulers/tetrisched/include/tetrisched/GoogleCPSolver.hpp b/schedulers/tetrisched/include/tetrisched/GoogleCPSolver.hpp index e4aec033..d20daeec 100644 --- a/schedulers/tetrisched/include/tetrisched/GoogleCPSolver.hpp +++ b/schedulers/tetrisched/include/tetrisched/GoogleCPSolver.hpp @@ -45,6 +45,9 @@ class GoogleCPSolver : public Solver { /// Export the constructed model to the given file. void exportModel(const std::string& fileName) override; + + /// Solve the constructed model. + void solveModel() override; }; } // namespace tetrisched #endif // _TETRISCHED_GOOGLE_CP_SOLVER_HPP_ diff --git a/schedulers/tetrisched/include/tetrisched/GurobiSolver.hpp b/schedulers/tetrisched/include/tetrisched/GurobiSolver.hpp index 544596a4..ba7825a0 100644 --- a/schedulers/tetrisched/include/tetrisched/GurobiSolver.hpp +++ b/schedulers/tetrisched/include/tetrisched/GurobiSolver.hpp @@ -43,7 +43,9 @@ class GurobiSolver : public Solver { /// Export the constructed model to the given file. void exportModel(const std::string& fileName) override; + + /// Solve the constructed model. + void solveModel() override; }; } // namespace tetrisched #endif // _TETRISCHED_GUROBI_SOLVER_HPP_ - diff --git a/schedulers/tetrisched/include/tetrisched/Solver.hpp b/schedulers/tetrisched/include/tetrisched/Solver.hpp index febb191a..ac213cb8 100644 --- a/schedulers/tetrisched/include/tetrisched/Solver.hpp +++ b/schedulers/tetrisched/include/tetrisched/Solver.hpp @@ -18,6 +18,9 @@ class Solver { /// Export the constructed model to the given file. virtual void exportModel(const std::string& fileName) = 0; + + /// Solve the constructed model. + virtual void solveModel() = 0; }; } // namespace tetrisched #endif // _TETRISCHED_SOLVER_HPP_ diff --git a/schedulers/tetrisched/include/tetrisched/SolverModel.hpp b/schedulers/tetrisched/include/tetrisched/SolverModel.hpp index 85f3dc9f..69645d35 100644 --- a/schedulers/tetrisched/include/tetrisched/SolverModel.hpp +++ b/schedulers/tetrisched/include/tetrisched/SolverModel.hpp @@ -43,6 +43,9 @@ class VariableT { /// An optional upper-bound for the variable. /// If unspecified, the solver will choose the upper bound for the given T. std::optional upperBound; + /// An optional solution value for the variable. + /// If unspecified, the solver has not found a solution for this problem yet. + std::optional solutionValue; /// Checks if the VariableType is valid. /// Throws an exception if the VariableType is invalid. /// Returns the type if it is valid. @@ -78,6 +81,11 @@ class VariableT { /// Retrieve the ID of this VariableT. uint32_t getId() const; + /// Retrieve the solution value for this VariableT. + /// If the solution value is not set, then the solver hasn't found a solution + /// (yet). + std::optional getValue() const; + /// Annotate friend classes for Solvers so that they have access to internals. friend tetrisched::CPLEXSolver; friend tetrisched::GurobiSolver; diff --git a/schedulers/tetrisched/src/CPLEXSolver.cpp b/schedulers/tetrisched/src/CPLEXSolver.cpp index 685a45a7..717a44c7 100644 --- a/schedulers/tetrisched/src/CPLEXSolver.cpp +++ b/schedulers/tetrisched/src/CPLEXSolver.cpp @@ -194,6 +194,38 @@ void CPLEXSolver::translateModel() { void CPLEXSolver::exportModel(const std::string& fname) { cplexInstance.exportModel(fname.c_str()); } + +void CPLEXSolver::solveModel() { + cplexInstance.solve(); + + // Retrieve all the variables from the CPLEX model into the SolverModel. + for (const auto& [variableId, variable] : solverModel->variables) { + if (cplexVariables.find(variableId) == cplexVariables.end()) { + throw tetrisched::exceptions::SolverException( + "Variable " + variable->getName() + " not found in CPLEX model."); + } + switch (variable->variableType) { + case tetrisched::VariableType::VAR_INTEGER: + variable->solutionValue = cplexInstance.getValue( + std::get(cplexVariables.at(variableId))); + break; + case tetrisched::VariableType::VAR_CONTINUOUS: + variable->solutionValue = cplexInstance.getValue( + std::get(cplexVariables.at(variableId))); + break; + case tetrisched::VariableType::VAR_INDICATOR: + variable->solutionValue = cplexInstance.getValue( + std::get(cplexVariables.at(variableId))); + break; + default: + throw tetrisched::exceptions::SolverException( + "Unsupported variable type: " + variable->variableType); + } + } +} + +CPLEXSolver::~CPLEXSolver() { cplexEnv.end(); } + } // namespace tetrisched // // Spend at least timeLimit sec. on optimization, but once // // this limit is reached, quit as soon as the solution is acceptable diff --git a/schedulers/tetrisched/src/GoogleCPSolver.cpp b/schedulers/tetrisched/src/GoogleCPSolver.cpp index ceebe0e0..cd19b7e5 100644 --- a/schedulers/tetrisched/src/GoogleCPSolver.cpp +++ b/schedulers/tetrisched/src/GoogleCPSolver.cpp @@ -162,4 +162,8 @@ void GoogleCPSolver::exportModel(const std::string& fileName) { cpModel->ExportToFile(fileName); } +void GoogleCPSolver::solveModel() { + throw exceptions::SolverException("Not implemented yet!"); +} + } // namespace tetrisched diff --git a/schedulers/tetrisched/src/GurobiSolver.cpp b/schedulers/tetrisched/src/GurobiSolver.cpp index a228fe9f..70de76db 100644 --- a/schedulers/tetrisched/src/GurobiSolver.cpp +++ b/schedulers/tetrisched/src/GurobiSolver.cpp @@ -133,5 +133,8 @@ void GurobiSolver::translateModel() { void GurobiSolver::exportModel(const std::string& fileName) { gurobiModel->write(fileName); } -} // namespace tetrisched +void GurobiSolver::solveModel() { + throw exceptions::SolverException("Not implemented yet!"); +} +} // namespace tetrisched diff --git a/schedulers/tetrisched/src/SolverModel.cpp b/schedulers/tetrisched/src/SolverModel.cpp index 289fe86a..796cb3f9 100644 --- a/schedulers/tetrisched/src/SolverModel.cpp +++ b/schedulers/tetrisched/src/SolverModel.cpp @@ -73,6 +73,11 @@ uint32_t VariableT::getId() const { return variableId; } +template +std::optional VariableT::getValue() const { + return solutionValue; +} + /* * Methods for Constraint. * These methods provide an implementation of the Constraint class. diff --git a/schedulers/tetrisched/test/test_solver.cpp b/schedulers/tetrisched/test/test_solver.cpp index 3794c086..75499679 100644 --- a/schedulers/tetrisched/test/test_solver.cpp +++ b/schedulers/tetrisched/test/test_solver.cpp @@ -8,9 +8,9 @@ #ifdef _TETRISCHED_WITH_GUROBI_ #include "tetrisched/GurobiSolver.hpp" #endif //_TETRISCHED_WITH_GUROBI_ -// #ifdef _TETRISCHED_WITH_OR_TOOLS_ -// #endif //_TETRISCHED_WITH_OR_TOOLS_ +#ifdef _TETRISCHED_WITH_OR_TOOLS_ #include "tetrisched/GoogleCPSolver.hpp" +#endif //_TETRISCHED_WITH_OR_TOOLS_ #include "tetrisched/Solver.hpp" #include "tetrisched/SolverModel.hpp" @@ -50,7 +50,8 @@ TEST(SolverModelTypes, TestObjectiveFnConstruction) { EXPECT_EQ(objectiveFn.size(), 1); } -void constructModel(tetrisched::SolverModelPtr& solverModelPtr) { +tetrisched::VariablePtr constructModel( + tetrisched::SolverModelPtr& solverModelPtr) { auto intVar = std::make_shared(tetrisched::VAR_INTEGER, "intVar", 0, 100); solverModelPtr->addVariable(intVar); @@ -63,6 +64,7 @@ void constructModel(tetrisched::SolverModelPtr& solverModelPtr) { tetrisched::ObjectiveType::OBJ_MAXIMIZE); objectiveFunction->addTerm(1, intVar); solverModelPtr->setObjectiveFunction(std::move(objectiveFunction)); + return intVar; } #ifdef _TETRISCHED_WITH_CPLEX_ @@ -94,7 +96,7 @@ TEST(SolverModelTypes, TestSolverModel) { TEST(SolverModel, TestCPLEXSolverTranslation) { tetrisched::CPLEXSolver cplexSolver; auto solverModelPtr = cplexSolver.getModel(); - constructModel(solverModelPtr); + auto intVar = constructModel(solverModelPtr); solverModelPtr->exportModel("test_solvermodel.lp"); EXPECT_TRUE(std::filesystem::exists("test_solvermodel.lp")) << "The file test_solvermodel.lp was not created."; @@ -104,6 +106,14 @@ TEST(SolverModel, TestCPLEXSolverTranslation) { EXPECT_TRUE(std::filesystem::exists("test_cplexmodel.lp")) << "The file test_cplexmodel.lp was not created."; std::filesystem::remove("test_cplexmodel.lp"); + + // Solve the model. + cplexSolver.solveModel(); + + // Check if solution is correct. + auto solutionValue = intVar->getValue(); + EXPECT_TRUE(solutionValue.has_value()) << "No solution found."; + EXPECT_EQ(solutionValue.value(), 2) << "Solution is not correct."; } #endif //_TETRISCHED_WITH_CPLEX_