diff --git a/CHANGELOG b/CHANGELOG index 1aa71ac7..7092dc58 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -14,6 +14,7 @@ code quality: fixed bugs: - remove unused and incorrect SOPLEX_VERSION_API in cmake, SOPLEX_APIVERSION now always set in `spxdefines.h` - disable objective limits once if exceeding unscaled and unsimplified solution is not dual feasible +- recompute solution from fresh factorization if objective limit is reached to avoid incorrect terminations miscellaneous: - remove `gzstream.h/hpp` (L-GPL) and replace it with `zstr` (MIT License) for `.gz` file handling diff --git a/src/soplex/spxsolve.hpp b/src/soplex/spxsolve.hpp index 3357e2fa..4d566418 100644 --- a/src/soplex/spxsolve.hpp +++ b/src/soplex/spxsolve.hpp @@ -445,23 +445,8 @@ typename SPxSolverBase::Status SPxSolverBase::solve(volatile bool* interru { R newpricertol = minpricertol; - // refactorize to eliminate accumulated errors from LU updates - if(this->lastUpdate() > 0) - factorize(); - - // recompute Fvec, Pvec and CoPvec to get a more precise solution and obj value - computeFrhs(); - SPxBasisBase::solve(*theFvec, *theFrhs); - - computeEnterCoPrhs(); - SPxBasisBase::coSolve(*theCoPvec, *theCoPrhs); - computePvec(); - - forceRecompNonbasicValue(); - - SPX_MSG_INFO2((*this->spxout), (*this->spxout) << " --- checking feasibility and optimality\n") - computeCoTest(); - computeTest(); + // ensure that solution is accurate + factorizeAndRecompute(); // is the solution good enough ? // max three times reduced @@ -802,22 +787,8 @@ typename SPxSolverBase::Status SPxSolverBase::solve(volatile bool* interru { R newpricertol = minpricertol; - // refactorize to eliminate accumulated errors from LU updates - if(this->lastUpdate() > 0) - factorize(); - - // recompute Fvec, Pvec and CoPvec to get a more precise solution and obj value - computeFrhs(); - SPxBasisBase::solve(*theFvec, *theFrhs); - - computeLeaveCoPrhs(); - SPxBasisBase::coSolve(*theCoPvec, *theCoPrhs); - computePvec(); - - forceRecompNonbasicValue(); - - SPX_MSG_INFO2((*this->spxout), (*this->spxout) << " --- checking feasibility and optimality\n") - computeFtest(); + // ensure that solution is accurate + factorizeAndRecompute(); // is the solution good enough ? // max three times reduced @@ -1863,15 +1834,23 @@ bool SPxSolverBase::terminate() // SPxSense::MINIMIZE == -1, so we have sign = 1 on minimizing if(int(this->spxSense()) * value() <= int(this->spxSense()) * objLimit) { - SPX_MSG_INFO2((*this->spxout), (*this->spxout) << " --- objective value limit (" << objLimit - << ") reached" << std::endl;) - SPxOut::debug(this, " --- objective value limit reached\n (value:{} limit:{})\n", value(), - objLimit); - SPxOut::debug(this, " (spxSense:{} rep:{} type:{})\n", int(this->spxSense()), int(rep()), - int(type())); - - m_status = ABORT_VALUE; - return true; + // ensure that solution is accurate + factorizeAndRecompute(); + + // check no violations and objective limit again + if(shift() < epsilon() && noViols(tolerances()->floatingPointOpttol() - shift()) + && int(this->spxSense()) * value() <= int(this->spxSense()) * objLimit) + { + SPX_MSG_INFO2((*this->spxout), (*this->spxout) << " --- objective value limit (" << objLimit + << ") reached" << std::endl;) + SPxOut::debug(this, " --- objective value limit reached\n (value:{} limit:{})\n", value(), + objLimit); + SPxOut::debug(this, " (spxSense:{} rep:{} type:{})\n", int(this->spxSense()), int(rep()), + int(type())); + + m_status = ABORT_VALUE; + return true; + } } } } diff --git a/src/soplex/spxsolver.h b/src/soplex/spxsolver.h index c9252c16..11750cef 100644 --- a/src/soplex/spxsolver.h +++ b/src/soplex/spxsolver.h @@ -728,6 +728,17 @@ class SPxSolverBase : public SPxLPBase, protected SPxBasisBase m_nonbasicValueUpToDate = false; } + /** helper method that computes a fresh factorization of the basis matrix + * (if at least one update has been performed) + * and recomputes Frhs, Fvec, CoPrhs, Pvec, and the nonbasic values. + * In LEAVE the Ftest is recomputed, in ENTER the CoTest and Test are recomputed. + * + * This method is called to eliminate accumulated errors from LU updates + * especially required before checking if the solver can terminate + * (optimality or objective limit) + */ + virtual void factorizeAndRecompute(); + /// get solution vector for primal variables. /** This method returns the Status of the basis. * If it is #REGULAR or better, diff --git a/src/soplex/spxsolver.hpp b/src/soplex/spxsolver.hpp index dd072452..4feea091 100644 --- a/src/soplex/spxsolver.hpp +++ b/src/soplex/spxsolver.hpp @@ -1025,6 +1025,33 @@ void SPxSolverBase::setType(Type tp) return m_nonbasicValueUpToDate; } + template + void SPxSolverBase::factorizeAndRecompute() + { + // refactorize to eliminate accumulated errors from LU updates + if(this->lastUpdate() > 0) + factorize(); + + computeFrhs(); + SPxBasisBase::solve(*theFvec, *theFrhs); + type() == LEAVE ? computeLeaveCoPrhs() : computeEnterCoPrhs(); + + SPxBasisBase::coSolve(*theCoPvec, *theCoPrhs); + computePvec(); + + forceRecompNonbasicValue(); + + SPX_MSG_INFO2((*this->spxout), (*this->spxout) << " --- checking feasibility and optimality\n") + + if(type() == LEAVE) + computeFtest(); + else + { + computeCoTest(); + computeTest(); + } + } + template void SPxSolverBase::hyperPricing(bool h) {