diff --git a/ThirdParty/HiGHS b/ThirdParty/HiGHS index b3381829..624bc58d 160000 --- a/ThirdParty/HiGHS +++ b/ThirdParty/HiGHS @@ -1 +1 @@ -Subproject commit b3381829e67eaea53637586a612164e296a7ac97 +Subproject commit 624bc58d3f2fdbc7f0c1e279da4d46779d04a13e diff --git a/src/MIPSolver/MIPSolverHighs.cpp b/src/MIPSolver/MIPSolverHighs.cpp index 65da20ff..6270deb1 100644 --- a/src/MIPSolver/MIPSolverHighs.cpp +++ b/src/MIPSolver/MIPSolverHighs.cpp @@ -39,6 +39,41 @@ static void highsLogCallback(HighsLogType type, const char* message, void* envPt env->output->outputInfo(fmt::format(" | {} ", line)); } +static void userInterruptCallback(const int callback_type, const char* message, const HighsCallbackDataOut* data_out, + HighsCallbackDataIn* data_in, void* user_callback_data) +{ + auto env = *reinterpret_cast*>(user_callback_data); + auto MIPSolver = std::dynamic_pointer_cast(env->dualSolver->MIPSolver); + + if(callback_type == kCallBackMipFeasibleSolution) + { + std::vector solution( + data_out->mip_solution, data_out->mip_solution + MIPSolver->getNumberOfVariables()); + + MIPSolver->currentSolutions.push_back(std::make_pair(data_out->objective_function_value, solution)); + + std::cout << "Sol limit " << MIPSolver->getSolutionLimit() << " number sols " + << MIPSolver->currentSolutions.size() << std::endl; + + data_in->user_interrupt = false; + } + else if(callback_type == kCallbackMipInterrupt) + { + // std::cout << "Sol limit " << MIPSolver->getSolutionLimit() << " number sols " + // << MIPSolver->currentSolutions.size() << std::endl; + + if(MIPSolver->currentSolutions.size() >= MIPSolver->getSolutionLimit()) + { + std::cout << "sol limit reached\n"; + data_in->user_interrupt = false; + } + else + { + data_in->user_interrupt = false; + } + } +} + MIPSolverHighs::MIPSolverHighs(EnvironmentPtr envPtr) { env = envPtr; } MIPSolverHighs::~MIPSolverHighs() = default; @@ -239,7 +274,7 @@ void MIPSolverHighs::initializeSolverSettings() "mip_abs_gap", env->settings->getSetting("ObjectiveGap.Absolute", "Termination")); highsInstance.setOptionValue( "mip_feasibility_tolerance", env->settings->getSetting("Tolerance.Integer", "Primal")); - highsInstance.setOptionValue("mip_heuristic_effort", 1.0); + // highsInstance.setOptionValue("mip_heuristic_effort", 1.0); // Adds a user-provided node limit if(auto nodeLimit = env->settings->getSetting("MIP.NodeLimit", "Dual"); nodeLimit > 0) @@ -256,6 +291,14 @@ void MIPSolverHighs::initializeSolverSettings() highsInstance.setOptionValue("threads", env->settings->getSetting("MIP.NumberOfThreads", "Dual")); highsInstance.setLogCallback(highsLogCallback, (void*)env.get()); + + // void* p_user_callback_data = (void*)(env.get()); + + auto ptr1 = reinterpret_cast(&env); /* shared_ptr > void ptr */ + + highsInstance.setCallback(userInterruptCallback, ptr1); + highsInstance.startCallback(kCallbackMipInterrupt); + highsInstance.startCallback(kCallBackMipFeasibleSolution); } int MIPSolverHighs::addLinearConstraint( @@ -388,6 +431,11 @@ E_ProblemSolutionStatus MIPSolverHighs::getSolutionStatus() { MIPSolutionStatus = E_ProblemSolutionStatus::SolutionLimit; } + else if(modelStatus == HighsModelStatus::kInterrupt) + { + // Since we interrup in the callback + MIPSolutionStatus = E_ProblemSolutionStatus::SolutionLimit; + } else { MIPSolutionStatus = E_ProblemSolutionStatus::Error; @@ -402,6 +450,7 @@ E_ProblemSolutionStatus MIPSolverHighs::solveProblem() { E_ProblemSolutionStatus MIPSolutionStatus; cachedSolutionHasChanged = true; + currentSolutions.clear(); highsReturnStatus = highsInstance.run(); MIPSolutionStatus = getSolutionStatus(); @@ -437,7 +486,6 @@ void MIPSolverHighs::setSolutionLimit(long int limit) else { highsInstance.setOptionValue("mip_max_improving_sols", (int)limit); - this->solLimit = limit; } } @@ -546,17 +594,17 @@ void MIPSolverHighs::writeProblemToFile(std::string filename) double MIPSolverHighs::getObjectiveValue(int solIdx) { + double objectiveValue; bool isMIP = getDiscreteVariableStatus(); - if(!isMIP && solIdx > 0) // LP problems only have one solution! + if(isProblemDiscrete && isMIP) { - env->output->outputError(" Cannot obtain solution with index " + std::to_string(solIdx) - + " in HiGHS since the problem is LP/QP!"); - - return (NAN); + objectiveValue = currentSolutions.at(currentSolutions.size() - solIdx - 1).first; + } + else + { + objectiveValue = highsInstance.getInfo().objective_function_value; } - - double objectiveValue = highsInstance.getInfo().objective_function_value; return (objectiveValue); } @@ -575,14 +623,39 @@ bool MIPSolverHighs::createIntegerCut(IntegerCut& integerCut) VectorDouble MIPSolverHighs::getVariableSolution(int solIdx) { - auto solution = highsInstance.getSolution().col_value; + // auto solution = highsInstance.getSolution().col_value; + // return (solution); + VectorDouble solution; + bool isMIP = getDiscreteVariableStatus(); + + if(isProblemDiscrete && isMIP) + { + solution = currentSolutions.at(currentSolutions.size() - solIdx - 1).second; + } + else + { + solution = highsInstance.getSolution().col_value; + } + return (solution); } int MIPSolverHighs::getNumberOfSolutions() { - // TODO: fix when solution pool functionality is available in HiGHS - int numSols = 1; + int numSols = 0; + bool isMIP = getDiscreteVariableStatus(); + + if(isProblemDiscrete && isMIP) + { + numSols = currentSolutions.size(); + } + else + { + // LP problem + numSols = 1; + // TODO better way? + } + return (numSols); } diff --git a/src/MIPSolver/MIPSolverHighs.h b/src/MIPSolver/MIPSolverHighs.h index ef0d701a..802ee0fc 100644 --- a/src/MIPSolver/MIPSolverHighs.h +++ b/src/MIPSolver/MIPSolverHighs.h @@ -157,6 +157,8 @@ class MIPSolverHighs : public IMIPSolver, MIPSolverBase std::string getSolverVersion() override; + std::vector> currentSolutions; + private: HighsModel highsModel; Highs highsInstance;