/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* */ /* This file is part of the class library */ /* SoPlex --- the Sequential object-oriented simPlex. */ /* */ /* Copyright 1996-2022 Zuse Institute Berlin */ /* */ /* Licensed under the Apache License, Version 2.0 (the "License"); */ /* you may not use this file except in compliance with the License. */ /* You may obtain a copy of the License at */ /* */ /* http://www.apache.org/licenses/LICENSE-2.0 */ /* */ /* Unless required by applicable law or agreed to in writing, software */ /* distributed under the License is distributed on an "AS IS" BASIS, */ /* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ /* See the License for the specific language governing permissions and */ /* limitations under the License. */ /* */ /* You should have received a copy of the Apache-2.0 license */ /* along with SoPlex; see the file LICENSE. If not email to soplex@zib.de. */ /* */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include #include #include "soplex.h" #include "soplex/statistics.h" #include "soplex/slufactor_rational.h" #include "soplex/ratrecon.h" namespace soplex { /// solves rational LP template void SoPlexBase::_optimizeRational(volatile bool* interrupt) { #ifndef SOPLEX_WITH_BOOST MSG_ERROR(std::cerr << "ERROR: rational solve without Boost not defined!" << std::endl;) return; #else bool hasUnboundedRay = false; bool infeasibilityNotCertified = false; bool unboundednessNotCertified = false; // start timing _statistics->solvingTime->start(); _statistics->preprocessingTime->start(); // remember that last solve was rational _lastSolveMode = SOLVEMODE_RATIONAL; // ensure that the solver has the original problemo if(!_isRealLPLoaded) { assert(_realLP != &_solver); _solver.loadLP(*_realLP); spx_free(_realLP); _realLP = &_solver; _isRealLPLoaded = true; } // during the rational solve, we always store basis information in the basis arrays else if(_hasBasis) { _basisStatusRows.reSize(numRows()); _basisStatusCols.reSize(numCols()); _solver.getBasis(_basisStatusRows.get_ptr(), _basisStatusCols.get_ptr(), _basisStatusRows.size(), _basisStatusCols.size()); } // store objective, bounds, and sides of Real LP in case they will be modified during iterative refinement _storeLPReal(); // deactivate objective limit in floating-point solver if(realParam(SoPlexBase::OBJLIMIT_LOWER) > -realParam(SoPlexBase::INFTY) || realParam(SoPlexBase::OBJLIMIT_UPPER) < realParam(SoPlexBase::INFTY)) { MSG_INFO2(spxout, spxout << "Deactivating objective limit.\n"); } _solver.setTerminationValue(realParam(SoPlexBase::INFTY)); _statistics->preprocessingTime->stop(); // apply lifting to reduce range of nonzero matrix coefficients if(boolParam(SoPlexBase::LIFTING)) _lift(); // force column representation ///@todo implement row objectives with row representation int oldRepresentation = intParam(SoPlexBase::REPRESENTATION); setIntParam(SoPlexBase::REPRESENTATION, SoPlexBase::REPRESENTATION_COLUMN); // force ratio test (avoid bound flipping) int oldRatiotester = intParam(SoPlexBase::RATIOTESTER); setIntParam(SoPlexBase::RATIOTESTER, SoPlexBase::RATIOTESTER_FAST); ///@todo implement handling of row objectives in Cplex interface #ifdef SOPLEX_WITH_CPX int oldEqtrans = boolParam(SoPlexBase::EQTRANS); setBoolParam(SoPlexBase::EQTRANS, true); #endif // introduce slack variables to transform inequality constraints into equations if(boolParam(SoPlexBase::EQTRANS)) _transformEquality(); _storedBasis = false; bool stoppedTime; bool stoppedIter; do { bool primalFeasible = false; bool dualFeasible = false; bool infeasible = false; bool unbounded = false; bool error = false; stoppedTime = false; stoppedIter = false; // solve problem with iterative refinement and recovery mechanism _performOptIRStable(_solRational, !unboundednessNotCertified, !infeasibilityNotCertified, 0, primalFeasible, dualFeasible, infeasible, unbounded, stoppedTime, stoppedIter, error); // case: an unrecoverable error occured if(error) { _status = SPxSolverBase::ERROR; break; } // case: stopped due to some limit else if(stoppedTime) { _status = SPxSolverBase::ABORT_TIME; break; } else if(stoppedIter) { _status = SPxSolverBase::ABORT_ITER; break; } // case: unboundedness detected for the first time else if(unbounded && !unboundednessNotCertified) { SolRational solUnbounded; _performUnboundedIRStable(solUnbounded, hasUnboundedRay, stoppedTime, stoppedIter, error); assert(!hasUnboundedRay || solUnbounded.hasPrimalRay()); assert(!solUnbounded.hasPrimalRay() || hasUnboundedRay); if(error) { MSG_INFO1(spxout, spxout << "Error while testing for unboundedness.\n"); _status = SPxSolverBase::ERROR; break; } if(hasUnboundedRay) { MSG_INFO1(spxout, spxout << "Dual infeasible. Primal unbounded ray available.\n"); } else { MSG_INFO1(spxout, spxout << "Dual feasible. Rejecting primal unboundedness.\n"); } unboundednessNotCertified = !hasUnboundedRay; if(stoppedTime) { _status = SPxSolverBase::ABORT_TIME; break; } else if(stoppedIter) { _status = SPxSolverBase::ABORT_ITER; break; } _performFeasIRStable(_solRational, infeasible, stoppedTime, stoppedIter, error); ///@todo this should be stored already earlier, possible switch use solRational above and solFeas here if(hasUnboundedRay) { _solRational._primalRay = solUnbounded._primalRay; _solRational._hasPrimalRay = true; } if(error) { MSG_INFO1(spxout, spxout << "Error while testing for feasibility.\n"); _status = SPxSolverBase::ERROR; break; } else if(stoppedTime) { _status = SPxSolverBase::ABORT_TIME; break; } else if(stoppedIter) { _status = SPxSolverBase::ABORT_ITER; break; } else if(infeasible) { MSG_INFO1(spxout, spxout << "Primal infeasible. Dual Farkas ray available.\n"); _status = SPxSolverBase::INFEASIBLE; break; } else if(hasUnboundedRay) { MSG_INFO1(spxout, spxout << "Primal feasible and unbounded.\n"); _status = SPxSolverBase::UNBOUNDED; break; } else { MSG_INFO1(spxout, spxout << "Primal feasible and bounded.\n"); continue; } } // case: infeasibility detected else if(infeasible && !infeasibilityNotCertified) { _storeBasis(); _performFeasIRStable(_solRational, infeasible, stoppedTime, stoppedIter, error); if(error) { MSG_INFO1(spxout, spxout << "Error while testing for infeasibility.\n"); _status = SPxSolverBase::ERROR; _restoreBasis(); break; } infeasibilityNotCertified = !infeasible; if(stoppedTime) { _status = SPxSolverBase::ABORT_TIME; _restoreBasis(); break; } else if(stoppedIter) { _status = SPxSolverBase::ABORT_ITER; _restoreBasis(); break; } if(infeasible && boolParam(SoPlexBase::TESTDUALINF)) { SolRational solUnbounded; _performUnboundedIRStable(solUnbounded, hasUnboundedRay, stoppedTime, stoppedIter, error); assert(!hasUnboundedRay || solUnbounded.hasPrimalRay()); assert(!solUnbounded.hasPrimalRay() || hasUnboundedRay); if(error) { MSG_INFO1(spxout, spxout << "Error while testing for dual infeasibility.\n"); _status = SPxSolverBase::ERROR; _restoreBasis(); break; } if(hasUnboundedRay) { MSG_INFO1(spxout, spxout << "Dual infeasible. Primal unbounded ray available.\n"); _solRational._primalRay = solUnbounded._primalRay; _solRational._hasPrimalRay = true; } else if(solUnbounded._isDualFeasible) { MSG_INFO1(spxout, spxout << "Dual feasible. Storing dual multipliers.\n"); _solRational._dual = solUnbounded._dual; _solRational._redCost = solUnbounded._redCost; _solRational._isDualFeasible = true; } else { assert(false); MSG_INFO1(spxout, spxout << "Not dual infeasible.\n"); } } _restoreBasis(); if(infeasible) { MSG_INFO1(spxout, spxout << "Primal infeasible. Dual Farkas ray available.\n"); _status = SPxSolverBase::INFEASIBLE; break; } else if(hasUnboundedRay) { MSG_INFO1(spxout, spxout << "Primal feasible and unbounded.\n"); _status = SPxSolverBase::UNBOUNDED; break; } else { MSG_INFO1(spxout, spxout << "Primal feasible. Optimizing again.\n"); continue; } } else if(primalFeasible && dualFeasible) { MSG_INFO1(spxout, spxout << "Solved to optimality.\n"); _status = SPxSolverBase::OPTIMAL; break; } else { MSG_INFO1(spxout, spxout << "Terminating without success.\n"); break; } } while(!_isSolveStopped(stoppedTime, stoppedIter)); ///@todo set status to ABORT_VALUE if optimal solution exceeds objective limit if(_status == SPxSolverBase::OPTIMAL || _status == SPxSolverBase::INFEASIBLE || _status == SPxSolverBase::UNBOUNDED) _hasSolRational = true; // restore original problem if(boolParam(SoPlexBase::EQTRANS)) _untransformEquality(_solRational); #ifdef SOPLEX_WITH_CPX setBoolParam(SoPlexBase::EQTRANS, oldEqtrans); #endif // reset representation and ratio test setIntParam(SoPlexBase::REPRESENTATION, oldRepresentation); setIntParam(SoPlexBase::RATIOTESTER, oldRatiotester); // undo lifting if(boolParam(SoPlexBase::LIFTING)) _project(_solRational); // restore objective, bounds, and sides of Real LP in case they have been modified during iterative refinement _restoreLPReal(); // since the Real LP is loaded in the solver, we need to also pass the basis information to the solver if // available if(_hasBasis) { assert(_isRealLPLoaded); _solver.setBasis(_basisStatusRows.get_const_ptr(), _basisStatusCols.get_const_ptr()); _hasBasis = (_solver.basis().status() > SPxBasisBase::NO_PROBLEM); // since setBasis always sets the basis status to regular, we need to set it manually here switch(_status) { case SPxSolverBase::OPTIMAL: _solver.setBasisStatus(SPxBasisBase::OPTIMAL); break; case SPxSolverBase::INFEASIBLE: _solver.setBasisStatus(SPxBasisBase::INFEASIBLE); break; case SPxSolverBase::UNBOUNDED: _solver.setBasisStatus(SPxBasisBase::UNBOUNDED); break; default: break; } } // stop timing _statistics->solvingTime->stop(); #endif } /// solves current problem with iterative refinement and recovery mechanism template void SoPlexBase::_performOptIRStable( SolRational& sol, bool acceptUnbounded, bool acceptInfeasible, int minRounds, bool& primalFeasible, bool& dualFeasible, bool& infeasible, bool& unbounded, bool& stoppedTime, bool& stoppedIter, bool& error) { // start rational solving timing _statistics->rationalTime->start(); primalFeasible = false; dualFeasible = false; infeasible = false; unbounded = false; stoppedTime = false; stoppedIter = false; error = false; // set working tolerances in floating-point solver _solver.setFeastol(realParam(SoPlexBase::FPFEASTOL)); _solver.setOpttol(realParam(SoPlexBase::FPOPTTOL)); // declare vectors and variables typename SPxSolverBase::Status result = SPxSolverBase::UNKNOWN; _modLower.reDim(numColsRational(), false); _modUpper.reDim(numColsRational(), false); _modLhs.reDim(numRowsRational(), false); _modRhs.reDim(numRowsRational(), false); _modObj.reDim(numColsRational(), false); VectorBase primalReal(numColsRational()); VectorBase dualReal(numRowsRational()); Rational boundsViolation; Rational sideViolation; Rational redCostViolation; Rational dualViolation; Rational primalScale; Rational dualScale; Rational maxScale; // solve original LP MSG_INFO1(spxout, spxout << "Initial floating-point solve . . .\n"); if(_hasBasis) { assert(_basisStatusRows.size() == numRowsRational()); assert(_basisStatusCols.size() == numColsRational()); _solver.setBasis(_basisStatusRows.get_const_ptr(), _basisStatusCols.get_const_ptr()); _hasBasis = (_solver.basis().status() > SPxBasisBase::NO_PROBLEM); } for(int r = numRowsRational() - 1; r >= 0; r--) { assert(_solver.maxRowObj(r) == 0.0); } _statistics->rationalTime->stop(); result = _solveRealStable(acceptUnbounded, acceptInfeasible, primalReal, dualReal, _basisStatusRows, _basisStatusCols); // evaluate result switch(result) { case SPxSolverBase::OPTIMAL: MSG_INFO1(spxout, spxout << "Floating-point optimal.\n"); break; case SPxSolverBase::INFEASIBLE: MSG_INFO1(spxout, spxout << "Floating-point infeasible.\n"); // the floating-point solve returns a Farkas ray if and only if the simplifier was not used, which is exactly // the case when a basis could be returned if(_hasBasis) { sol._dualFarkas = dualReal; sol._hasDualFarkas = true; } else sol._hasDualFarkas = false; infeasible = true; return; case SPxSolverBase::UNBOUNDED: MSG_INFO1(spxout, spxout << "Floating-point unbounded.\n"); unbounded = true; return; case SPxSolverBase::ABORT_TIME: stoppedTime = true; return; case SPxSolverBase::ABORT_ITER: stoppedIter = true; return; default: error = true; return; } _statistics->rationalTime->start(); // store floating-point solution of original LP as current rational solution and ensure that solution vectors have // right dimension; ensure that solution is aligned with basis sol._primal.reDim(numColsRational(), false); sol._slacks.reDim(numRowsRational(), false); sol._dual.reDim(numRowsRational(), false); sol._redCost.reDim(numColsRational(), false); sol._isPrimalFeasible = true; sol._isDualFeasible = true; for(int c = numColsRational() - 1; c >= 0; c--) { typename SPxSolverBase::VarStatus& basisStatusCol = _basisStatusCols[c]; if(basisStatusCol == SPxSolverBase::ON_LOWER) sol._primal[c] = lowerRational(c); else if(basisStatusCol == SPxSolverBase::ON_UPPER) sol._primal[c] = upperRational(c); else if(basisStatusCol == SPxSolverBase::FIXED) { // it may happen that lower and upper are only equal in the Real LP but different in the rational LP; we do // not check this to avoid rational comparisons, but simply switch the basis status to the lower bound; this // is necessary, because for fixed variables any reduced cost is feasible sol._primal[c] = lowerRational(c); basisStatusCol = SPxSolverBase::ON_LOWER; } else if(basisStatusCol == SPxSolverBase::ZERO) sol._primal[c] = 0; else sol._primal[c].assign(primalReal[c]); } _rationalLP->computePrimalActivity(sol._primal, sol._slacks); int dualSize = 0; for(int r = numRowsRational() - 1; r >= 0; r--) { typename SPxSolverBase::VarStatus& basisStatusRow = _basisStatusRows[r]; // it may happen that left-hand and right-hand side are different in the rational, but equal in the Real LP, // leading to a fixed basis status; this is critical because rows with fixed basis status are ignored in the // computation of the dual violation; to avoid rational comparisons we do not check this but simply switch to // the left-hand side status if(basisStatusRow == SPxSolverBase::FIXED) basisStatusRow = SPxSolverBase::ON_LOWER; { sol._dual[r].assign(dualReal[r]); if(dualReal[r] != 0.0) dualSize++; } } // we assume that the objective function vector has less nonzeros than the reduced cost vector, and so multiplying // with -1 first and subtracting the dual activity should be faster than adding the dual activity and negating // afterwards _rationalLP->getObj(sol._redCost); _rationalLP->subDualActivity(sol._dual, sol._redCost); // initial scaling factors are one primalScale = _rationalPosone; dualScale = _rationalPosone; // control progress Rational maxViolation; Rational bestViolation = _rationalPosInfty; const Rational violationImprovementFactor = 16; const Rational errorCorrectionFactor = 1.1; Rational errorCorrection = 2; int numFailedRefinements = 0; // store basis status in case solving modified problem failed DataArray< typename SPxSolverBase::VarStatus > basisStatusRowsFirst; DataArray< typename SPxSolverBase::VarStatus > basisStatusColsFirst; // refinement loop const bool maximizing = (intParam(SoPlexBase::OBJSENSE) == SoPlexBase::OBJSENSE_MAXIMIZE); const int maxDimRational = numColsRational() > numRowsRational() ? numColsRational() : numRowsRational(); SolRational factorSol; bool factorSolNewBasis = true; int lastStallRefinements = 0; int nextRatrecRefinement = 0; do { // decrement minRounds counter minRounds--; MSG_DEBUG(std::cout << "Computing primal violations.\n"); // compute violation of bounds boundsViolation = 0; for(int c = numColsRational() - 1; c >= 0; c--) { // lower bound assert((lowerRational(c) > _rationalNegInfty) == _lowerFinite(_colTypes[c])); if(_lowerFinite(_colTypes[c])) { if(lowerRational(c) == 0) { _modLower[c] = sol._primal[c]; _modLower[c] *= -1; if(_modLower[c] > boundsViolation) boundsViolation = _modLower[c]; } else { _modLower[c] = lowerRational(c); _modLower[c] -= sol._primal[c]; if(_modLower[c] > boundsViolation) boundsViolation = _modLower[c]; } } // upper bound assert((upperRational(c) < _rationalPosInfty) == _upperFinite(_colTypes[c])); if(_upperFinite(_colTypes[c])) { if(upperRational(c) == 0) { _modUpper[c] = sol._primal[c]; _modUpper[c] *= -1; if(_modUpper[c] < -boundsViolation) boundsViolation = -_modUpper[c]; } else { _modUpper[c] = upperRational(c); _modUpper[c] -= sol._primal[c]; if(_modUpper[c] < -boundsViolation) boundsViolation = -_modUpper[c]; } } } // compute violation of sides sideViolation = 0; for(int r = numRowsRational() - 1; r >= 0; r--) { const typename SPxSolverBase::VarStatus& basisStatusRow = _basisStatusRows[r]; // left-hand side assert((lhsRational(r) > _rationalNegInfty) == _lowerFinite(_rowTypes[r])); if(_lowerFinite(_rowTypes[r])) { if(lhsRational(r) == 0) { _modLhs[r] = sol._slacks[r]; _modLhs[r] *= -1; } else { _modLhs[r] = lhsRational(r); _modLhs[r] -= sol._slacks[r]; } if(_modLhs[r] > sideViolation) sideViolation = _modLhs[r]; // if the activity is feasible, but too far from the bound, this violates complementary slackness; we // count it as side violation here else if(basisStatusRow == SPxSolverBase::ON_LOWER && _modLhs[r] < -sideViolation) sideViolation = -_modLhs[r]; } // right-hand side assert((rhsRational(r) < _rationalPosInfty) == _upperFinite(_rowTypes[r])); if(_upperFinite(_rowTypes[r])) { if(rhsRational(r) == 0) { _modRhs[r] = sol._slacks[r]; _modRhs[r] *= -1; } else { _modRhs[r] = rhsRational(r); _modRhs[r] -= sol._slacks[r]; } if(_modRhs[r] < -sideViolation) sideViolation = -_modRhs[r]; // if the activity is feasible, but too far from the bound, this violates complementary slackness; we // count it as side violation here else if(basisStatusRow == SPxSolverBase::ON_UPPER && _modRhs[r] > sideViolation) sideViolation = _modRhs[r]; } } MSG_DEBUG(std::cout << "Computing dual violations.\n"); // compute reduced cost violation redCostViolation = 0; for(int c = numColsRational() - 1; c >= 0; c--) { if(_colTypes[c] == RANGETYPE_FIXED) continue; const typename SPxSolverBase::VarStatus& basisStatusCol = _basisStatusCols[c]; assert(basisStatusCol != SPxSolverBase::FIXED); if(((maximizing && basisStatusCol != SPxSolverBase::ON_LOWER) || (!maximizing && basisStatusCol != SPxSolverBase::ON_UPPER)) && sol._redCost[c] < -redCostViolation) { MSG_DEBUG(std::cout << "basisStatusCol = " << basisStatusCol << ", lower tight = " << bool(sol._primal[c] <= lowerRational(c)) << ", upper tight = " << bool(sol._primal[c] >= upperRational(c)) << ", sol._redCost[c] = " << sol._redCost[c].str() << "\n"); redCostViolation = -sol._redCost[c]; } if(((maximizing && basisStatusCol != SPxSolverBase::ON_UPPER) || (!maximizing && basisStatusCol != SPxSolverBase::ON_LOWER)) && sol._redCost[c] > redCostViolation) { MSG_DEBUG(std::cout << "basisStatusCol = " << basisStatusCol << ", lower tight = " << bool(sol._primal[c] <= lowerRational(c)) << ", upper tight = " << bool(sol._primal[c] >= upperRational(c)) << ", sol._redCost[c] = " << sol._redCost[c].str() << "\n"); redCostViolation = sol._redCost[c]; } } // compute dual violation dualViolation = 0; for(int r = numRowsRational() - 1; r >= 0; r--) { if(_rowTypes[r] == RANGETYPE_FIXED) continue; const typename SPxSolverBase::VarStatus& basisStatusRow = _basisStatusRows[r]; assert(basisStatusRow != SPxSolverBase::FIXED); if(((maximizing && basisStatusRow != SPxSolverBase::ON_LOWER) || (!maximizing && basisStatusRow != SPxSolverBase::ON_UPPER)) && sol._dual[r] < -dualViolation) { MSG_DEBUG(std::cout << "basisStatusRow = " << basisStatusRow << ", lower tight = " << bool(sol._slacks[r] <= lhsRational(r)) << ", upper tight = " << bool(sol._slacks[r] >= rhsRational(r)) << ", sol._dual[r] = " << sol._dual[r].str() << "\n"); dualViolation = -sol._dual[r]; } if(((maximizing && basisStatusRow != SPxSolverBase::ON_UPPER) || (!maximizing && basisStatusRow != SPxSolverBase::ON_LOWER)) && sol._dual[r] > dualViolation) { MSG_DEBUG(std::cout << "basisStatusRow = " << basisStatusRow << ", lower tight = " << bool(sol._slacks[r] <= lhsRational(r)) << ", upper tight = " << bool(sol._slacks[r] >= rhsRational(r)) << ", sol._dual[r] = " << sol._dual[r].str() << "\n"); dualViolation = sol._dual[r]; } } _modObj = sol._redCost; // output violations; the reduced cost violations for artificially introduced slack columns are actually violations of the dual multipliers MSG_INFO1(spxout, spxout << "Max. bound violation = " << boundsViolation.str() << "\n" << "Max. row violation = " << sideViolation.str() << "\n" << "Max. reduced cost violation = " << redCostViolation.str() << "\n" << "Max. dual violation = " << dualViolation.str() << "\n"); MSG_DEBUG(spxout << std::fixed << std::setprecision(2) << std::setw(10) << "Progress table: " << std::setw(10) << _statistics->refinements << " & " << std::setw(10) << _statistics->iterations << " & " << std::setw(10) << _statistics->solvingTime->time() << " & " << std::setw(10) << _statistics->rationalTime->time() << " & " << std::setw(10) << boundsViolation > sideViolation ? boundsViolation : sideViolation << " & " << std::setw(10) << redCostViolation > dualViolation ? redCostViolation : dualViolation << "\n"); // terminate if tolerances are satisfied primalFeasible = (boundsViolation <= _rationalFeastol && sideViolation <= _rationalFeastol); dualFeasible = (redCostViolation <= _rationalOpttol && dualViolation <= _rationalOpttol); if(primalFeasible && dualFeasible) { if(minRounds < 0) { MSG_INFO1(spxout, spxout << "Tolerances reached.\n"); break; } else { MSG_INFO1(spxout, spxout << "Tolerances reached but minRounds forcing additional refinement rounds.\n"); } } // terminate if some limit is reached if(_isSolveStopped(stoppedTime, stoppedIter) || numFailedRefinements > 2) break; // check progress maxViolation = boundsViolation; if(sideViolation > maxViolation) maxViolation = sideViolation; if(redCostViolation > maxViolation) maxViolation = redCostViolation; if(dualViolation > maxViolation) maxViolation = dualViolation; bestViolation /= violationImprovementFactor; if(maxViolation > bestViolation) { MSG_INFO2(spxout, spxout << "Failed to reduce violation significantly.\n"); bestViolation *= violationImprovementFactor; numFailedRefinements++; } else bestViolation = maxViolation; // decide whether to perform rational reconstruction and/or factorization bool forcebasic = boolParam(SoPlexBase::FORCEBASIC); bool performRatfac = boolParam(SoPlexBase::RATFAC) && lastStallRefinements >= intParam(SoPlexBase::RATFAC_MINSTALLS) && _hasBasis && factorSolNewBasis; bool performRatrec = boolParam(SoPlexBase::RATREC) && (_statistics->refinements >= nextRatrecRefinement || performRatfac); // if we want to force the solution to be basic we need to turn rational factorization on performRatfac = performRatfac || forcebasic; // attempt rational reconstruction errorCorrection *= errorCorrectionFactor; if(performRatrec && maxViolation > 0) { MSG_INFO1(spxout, spxout << "Performing rational reconstruction . . .\n"); maxViolation *= errorCorrection; // only used for sign check later invert(maxViolation); if(_reconstructSolutionRational(sol, _basisStatusRows, _basisStatusCols, maxViolation)) { MSG_INFO1(spxout, spxout << "Tolerances reached.\n"); primalFeasible = true; dualFeasible = true; if(_hasBasis || !forcebasic) break; } nextRatrecRefinement = int(_statistics->refinements * realParam(SoPlexBase::RATREC_FREQ)) + 1; MSG_DEBUG(spxout << "Next rational reconstruction after refinement " << nextRatrecRefinement << ".\n"); } // solve basis systems exactly if((performRatfac && maxViolation > 0) || (!_hasBasis && forcebasic)) { MSG_INFO1(spxout, spxout << "Performing rational factorization . . .\n"); bool optimal; _factorizeColumnRational(sol, _basisStatusRows, _basisStatusCols, stoppedTime, stoppedIter, error, optimal); factorSolNewBasis = false; if(stoppedTime) { MSG_INFO1(spxout, spxout << "Stopped rational factorization.\n"); } else if(error) { // message was already printed; reset error flag and continue without factorization error = false; } else if(optimal) { MSG_INFO1(spxout, spxout << "Tolerances reached.\n"); primalFeasible = true; dualFeasible = true; break; } else if(boolParam(SoPlexBase::RATFACJUMP)) { MSG_INFO1(spxout, spxout << "Jumping to exact basic solution.\n"); minRounds++; continue; } } // start refinement // compute primal scaling factor; limit increase in scaling by tolerance used in floating point solve maxScale = primalScale; maxScale *= _rationalMaxscaleincr; primalScale = boundsViolation > sideViolation ? boundsViolation : sideViolation; if(primalScale < redCostViolation) primalScale = redCostViolation; assert(primalScale >= 0); if(primalScale > 0) { invert(primalScale); if(primalScale > maxScale) primalScale = maxScale; } else primalScale = maxScale; if(boolParam(SoPlexBase::POWERSCALING)) powRound(primalScale); // apply scaled bounds if(primalScale <= 1) { if(primalScale < 1) primalScale = 1; for(int c = numColsRational() - 1; c >= 0; c--) { if(_lowerFinite(_colTypes[c])) { if(_modLower[c] <= _rationalNegInfty) _solver.changeLower(c, -realParam(SoPlexBase::INFTY)); else _solver.changeLower(c, static_cast(_modLower[c])); } if(_upperFinite(_colTypes[c])) { if(_modUpper[c] >= _rationalPosInfty) _solver.changeUpper(c, realParam(SoPlexBase::INFTY)); else _solver.changeUpper(c, R(_modUpper[c])); } } } else { MSG_INFO2(spxout, spxout << "Scaling primal by " << primalScale.str() << ".\n"); for(int c = numColsRational() - 1; c >= 0; c--) { if(_lowerFinite(_colTypes[c])) { _modLower[c] *= primalScale; if(_modLower[c] <= _rationalNegInfty) _solver.changeLower(c, -realParam(SoPlexBase::INFTY)); else _solver.changeLower(c, R(_modLower[c])); } if(_upperFinite(_colTypes[c])) { _modUpper[c] *= primalScale; if(_modUpper[c] >= _rationalPosInfty) _solver.changeUpper(c, realParam(SoPlexBase::INFTY)); else _solver.changeUpper(c, R(_modUpper[c])); } } } // apply scaled sides assert(primalScale >= 1); if(primalScale == 1) { for(int r = numRowsRational() - 1; r >= 0; r--) { if(_lowerFinite(_rowTypes[r])) { if(_modLhs[r] <= _rationalNegInfty) _solver.changeLhs(r, -realParam(SoPlexBase::INFTY)); else _solver.changeLhs(r, R(_modLhs[r])); } if(_upperFinite(_rowTypes[r])) { if(_modRhs[r] >= _rationalPosInfty) _solver.changeRhs(r, realParam(SoPlexBase::INFTY)); else _solver.changeRhs(r, R(_modRhs[r])); } } } else { for(int r = numRowsRational() - 1; r >= 0; r--) { if(_lowerFinite(_rowTypes[r])) { _modLhs[r] *= primalScale; if(_modLhs[r] <= _rationalNegInfty) _solver.changeLhs(r, -realParam(SoPlexBase::INFTY)); else _solver.changeLhs(r, R(_modLhs[r])); } if(_upperFinite(_rowTypes[r])) { _modRhs[r] *= primalScale; if(_modRhs[r] >= _rationalPosInfty) _solver.changeRhs(r, realParam(SoPlexBase::INFTY)); else _solver.changeRhs(r, R(_modRhs[r])); } } } // compute dual scaling factor; limit increase in scaling by tolerance used in floating point solve maxScale = dualScale; maxScale *= _rationalMaxscaleincr; dualScale = redCostViolation > dualViolation ? redCostViolation : dualViolation; assert(dualScale >= 0); if(dualScale > 0) { invert(dualScale); if(dualScale > maxScale) dualScale = maxScale; } else dualScale = maxScale; if(boolParam(SoPlexBase::POWERSCALING)) powRound(dualScale); if(dualScale > primalScale) dualScale = primalScale; if(dualScale < 1) dualScale = 1; else { MSG_INFO2(spxout, spxout << "Scaling dual by " << dualScale.str() << ".\n"); // perform dual scaling ///@todo remove _modObj and use dualScale * sol._redCost directly _modObj *= dualScale; } // apply scaled objective function for(int c = numColsRational() - 1; c >= 0; c--) { if(_modObj[c] >= _rationalPosInfty) _solver.changeObj(c, realParam(SoPlexBase::INFTY)); else if(_modObj[c] <= _rationalNegInfty) _solver.changeObj(c, -realParam(SoPlexBase::INFTY)); else _solver.changeObj(c, R(_modObj[c])); } for(int r = numRowsRational() - 1; r >= 0; r--) { Rational newRowObj; if(_rowTypes[r] == RANGETYPE_FIXED) _solver.changeRowObj(r, R(0.0)); else { newRowObj = sol._dual[r]; newRowObj *= dualScale; if(newRowObj >= _rationalPosInfty) _solver.changeRowObj(r, -realParam(SoPlexBase::INFTY)); else if(newRowObj <= _rationalNegInfty) _solver.changeRowObj(r, realParam(SoPlexBase::INFTY)); else _solver.changeRowObj(r, -R(newRowObj)); } } MSG_INFO1(spxout, spxout << "Refined floating-point solve . . .\n"); // ensure that artificial slack columns are basic and inequality constraints are nonbasic; otherwise we may end // up with dual violation on inequality constraints after removing the slack columns; do not change this in the // floating-point solver, though, because the solver may require its original basis to detect optimality if(_slackCols.num() > 0 && _hasBasis) { int numOrigCols = numColsRational() - _slackCols.num(); assert(_slackCols.num() <= 0 || boolParam(SoPlexBase::EQTRANS)); for(int i = 0; i < _slackCols.num(); i++) { int row = _slackCols.colVector(i).index(0); int col = numOrigCols + i; assert(row >= 0); assert(row < numRowsRational()); if(_basisStatusRows[row] == SPxSolverBase::BASIC && _basisStatusCols[col] != SPxSolverBase::BASIC) { _basisStatusRows[row] = _basisStatusCols[col]; _basisStatusCols[col] = SPxSolverBase::BASIC; _rationalLUSolver.clear(); } } } // load basis if(_hasBasis && _solver.basis().status() < SPxBasisBase::REGULAR) { MSG_DEBUG(spxout << "basis (status = " << _solver.basis().status() << ") desc before set:\n"; _solver.basis().desc().dump()); _solver.setBasis(_basisStatusRows.get_const_ptr(), _basisStatusCols.get_const_ptr()); MSG_DEBUG(spxout << "basis (status = " << _solver.basis().status() << ") desc after set:\n"; _solver.basis().desc().dump()); _hasBasis = _solver.basis().status() > SPxBasisBase::NO_PROBLEM; MSG_DEBUG(spxout << "setting basis in solver " << (_hasBasis ? "successful" : "failed") << " (3)\n"); } // solve modified problem int prevIterations = _statistics->iterations; _statistics->rationalTime->stop(); result = _solveRealStable(acceptUnbounded, acceptInfeasible, primalReal, dualReal, _basisStatusRows, _basisStatusCols, primalScale > 1e20 || dualScale > 1e20); // count refinements and remember whether we moved to a new basis _statistics->refinements++; if(_statistics->iterations <= prevIterations) { lastStallRefinements++; _statistics->stallRefinements++; } else { factorSolNewBasis = true; lastStallRefinements = 0; _statistics->pivotRefinements = _statistics->refinements; } // evaluate result; if modified problem was not solved to optimality, stop refinement switch(result) { case SPxSolverBase::OPTIMAL: MSG_INFO1(spxout, spxout << "Floating-point optimal.\n"); break; case SPxSolverBase::INFEASIBLE: MSG_INFO1(spxout, spxout << "Floating-point infeasible.\n"); sol._dualFarkas = dualReal; sol._hasDualFarkas = true; infeasible = true; _solver.clearRowObjs(); return; case SPxSolverBase::UNBOUNDED: MSG_INFO1(spxout, spxout << "Floating-point unbounded.\n"); unbounded = true; _solver.clearRowObjs(); return; case SPxSolverBase::ABORT_TIME: stoppedTime = true; return; case SPxSolverBase::ABORT_ITER: stoppedIter = true; _solver.clearRowObjs(); return; default: error = true; _solver.clearRowObjs(); return; } _statistics->rationalTime->start(); // correct primal solution and align with basis MSG_DEBUG(std::cout << "Correcting primal solution.\n"); int primalSize = 0; Rational primalScaleInverse = primalScale; invert(primalScaleInverse); _primalDualDiff.clear(); for(int c = numColsRational() - 1; c >= 0; c--) { // force values of nonbasic variables to bounds typename SPxSolverBase::VarStatus& basisStatusCol = _basisStatusCols[c]; if(basisStatusCol == SPxSolverBase::ON_LOWER) { if(sol._primal[c] != lowerRational(c)) { int i = _primalDualDiff.size(); _ensureDSVectorRationalMemory(_primalDualDiff, maxDimRational); _primalDualDiff.add(c); _primalDualDiff.value(i) = lowerRational(c); _primalDualDiff.value(i) -= sol._primal[c]; sol._primal[c] = lowerRational(c); } } else if(basisStatusCol == SPxSolverBase::ON_UPPER) { if(sol._primal[c] != upperRational(c)) { int i = _primalDualDiff.size(); _ensureDSVectorRationalMemory(_primalDualDiff, maxDimRational); _primalDualDiff.add(c); _primalDualDiff.value(i) = upperRational(c); _primalDualDiff.value(i) -= sol._primal[c]; sol._primal[c] = upperRational(c); } } else if(basisStatusCol == SPxSolverBase::FIXED) { // it may happen that lower and upper are only equal in the Real LP but different in the rational LP; we // do not check this to avoid rational comparisons, but simply switch the basis status to the lower // bound; this is necessary, because for fixed variables any reduced cost is feasible basisStatusCol = SPxSolverBase::ON_LOWER; if(sol._primal[c] != lowerRational(c)) { int i = _primalDualDiff.size(); _ensureDSVectorRationalMemory(_primalDualDiff, maxDimRational); _primalDualDiff.add(c); _primalDualDiff.value(i) = lowerRational(c); _primalDualDiff.value(i) -= sol._primal[c]; sol._primal[c] = lowerRational(c); } } else if(basisStatusCol == SPxSolverBase::ZERO) { if(sol._primal[c] != 0) { int i = _primalDualDiff.size(); _ensureDSVectorRationalMemory(_primalDualDiff, maxDimRational); _primalDualDiff.add(c); _primalDualDiff.value(i) = sol._primal[c]; _primalDualDiff.value(i) *= -1; sol._primal[c] = 0; } } else { if(primalReal[c] == 1.0) { int i = _primalDualDiff.size(); _ensureDSVectorRationalMemory(_primalDualDiff, maxDimRational); _primalDualDiff.add(c); _primalDualDiff.value(i) = primalScaleInverse; sol._primal[c] += _primalDualDiff.value(i); } else if(primalReal[c] == -1.0) { int i = _primalDualDiff.size(); _ensureDSVectorRationalMemory(_primalDualDiff, maxDimRational); _primalDualDiff.add(c); _primalDualDiff.value(i) = primalScaleInverse; _primalDualDiff.value(i) *= -1; sol._primal[c] += _primalDualDiff.value(i); } else if(primalReal[c] != 0.0) { int i = _primalDualDiff.size(); _ensureDSVectorRationalMemory(_primalDualDiff, maxDimRational); _primalDualDiff.add(c); _primalDualDiff.value(i).assign(primalReal[c]); _primalDualDiff.value(i) *= primalScaleInverse; sol._primal[c] += _primalDualDiff.value(i); } } if(sol._primal[c] != 0) primalSize++; } // update or recompute slacks depending on which looks faster if(_primalDualDiff.size() < primalSize) { _rationalLP->addPrimalActivity(_primalDualDiff, sol._slacks); #ifndef NDEBUG { VectorRational activity(numRowsRational()); _rationalLP->computePrimalActivity(sol._primal, activity); assert(sol._slacks == activity); } #endif } else _rationalLP->computePrimalActivity(sol._primal, sol._slacks); const int numCorrectedPrimals = _primalDualDiff.size(); // correct dual solution and align with basis MSG_DEBUG(std::cout << "Correcting dual solution.\n"); #ifndef NDEBUG { // compute reduced cost violation VectorRational debugRedCost(numColsRational()); debugRedCost = VectorRational(_realLP->maxObj()); debugRedCost *= -1; _rationalLP->subDualActivity(VectorRational(dualReal), debugRedCost); Rational debugRedCostViolation = 0; for(int c = numColsRational() - 1; c >= 0; c--) { if(_colTypes[c] == RANGETYPE_FIXED) continue; const typename SPxSolverBase::VarStatus& basisStatusCol = _basisStatusCols[c]; assert(basisStatusCol != SPxSolverBase::FIXED); if(((maximizing && basisStatusCol != SPxSolverBase::ON_LOWER) || (!maximizing && basisStatusCol != SPxSolverBase::ON_UPPER)) && debugRedCost[c] < -debugRedCostViolation) { MSG_DEBUG(std::cout << "basisStatusCol = " << basisStatusCol << ", lower tight = " << bool(sol._primal[c] <= lowerRational(c)) << ", upper tight = " << bool(sol._primal[c] >= upperRational(c)) << ", obj[c] = " << _realLP->obj(c) << ", debugRedCost[c] = " << debugRedCost[c].str() << "\n"); debugRedCostViolation = -debugRedCost[c]; } if(((maximizing && basisStatusCol != SPxSolverBase::ON_UPPER) || (!maximizing && basisStatusCol != SPxSolverBase::ON_LOWER)) && debugRedCost[c] > debugRedCostViolation) { MSG_DEBUG(std::cout << "basisStatusCol = " << basisStatusCol << ", lower tight = " << bool(sol._primal[c] <= lowerRational(c)) << ", upper tight = " << bool(sol._primal[c] >= upperRational(c)) << ", obj[c] = " << _realLP->obj(c) << ", debugRedCost[c] = " << debugRedCost[c].str() << "\n"); debugRedCostViolation = debugRedCost[c]; } } // compute dual violation Rational debugDualViolation = 0; Rational debugBasicDualViolation = 0; for(int r = numRowsRational() - 1; r >= 0; r--) { if(_rowTypes[r] == RANGETYPE_FIXED) continue; const typename SPxSolverBase::VarStatus& basisStatusRow = _basisStatusRows[r]; assert(basisStatusRow != SPxSolverBase::FIXED); Rational val = (-dualScale * sol._dual[r]) - Rational(dualReal[r]); if(((maximizing && basisStatusRow != SPxSolverBase::ON_LOWER) || (!maximizing && basisStatusRow != SPxSolverBase::ON_UPPER)) && val > debugDualViolation) { MSG_DEBUG(std::cout << "basisStatusRow = " << basisStatusRow << ", lower tight = " << bool(sol._slacks[r] <= lhsRational(r)) << ", upper tight = " << bool(sol._slacks[r] >= rhsRational(r)) << ", dualReal[r] = " << val.str() << ", dualReal[r] = " << dualReal[r] << "\n"); debugDualViolation = val; } if(((maximizing && basisStatusRow != SPxSolverBase::ON_UPPER) || (!maximizing && basisStatusRow != SPxSolverBase::ON_LOWER)) && val < -debugDualViolation) { MSG_DEBUG(std::cout << "basisStatusRow = " << basisStatusRow << ", lower tight = " << bool(sol._slacks[r] <= lhsRational(r)) << ", upper tight = " << bool(sol._slacks[r] >= rhsRational(r)) << ", dualReal[r] = " << val.str() << ", dualReal[r] = " << dualReal[r] << "\n"); debugDualViolation = -val; } if(basisStatusRow == SPxSolverBase::BASIC && spxAbs(val) > debugBasicDualViolation) { MSG_DEBUG(std::cout << "basisStatusRow = " << basisStatusRow << ", lower tight = " << bool(sol._slacks[r] <= lhsRational(r)) << ", upper tight = " << bool(sol._slacks[r] >= rhsRational(r)) << ", dualReal[r] = " << val.str() << ", dualReal[r] = " << dualReal[r] << "\n"); debugBasicDualViolation = spxAbs(val); } } if(R(debugRedCostViolation) > _solver.opttol() || R(debugDualViolation) > _solver.opttol() || debugBasicDualViolation > 1e-9) { MSG_WARNING(spxout, spxout << "Warning: floating-point dual solution with violation " << debugRedCostViolation.str() << " / " << debugDualViolation.str() << " / " << debugBasicDualViolation.str() << " (red. cost, dual, basic).\n"); } } #endif Rational dualScaleInverseNeg = dualScale; invert(dualScaleInverseNeg); dualScaleInverseNeg *= -1; _primalDualDiff.clear(); dualSize = 0; for(int r = numRowsRational() - 1; r >= 0; r--) { typename SPxSolverBase::VarStatus& basisStatusRow = _basisStatusRows[r]; // it may happen that left-hand and right-hand side are different in the rational, but equal in the Real LP, // leading to a fixed basis status; this is critical because rows with fixed basis status are ignored in the // computation of the dual violation; to avoid rational comparisons we do not check this but simply switch // to the left-hand side status if(basisStatusRow == SPxSolverBase::FIXED) basisStatusRow = SPxSolverBase::ON_LOWER; { if(dualReal[r] != 0) { int i = _primalDualDiff.size(); _ensureDSVectorRationalMemory(_primalDualDiff, maxDimRational); _primalDualDiff.add(r); _primalDualDiff.value(i).assign(dualReal[r]); _primalDualDiff.value(i) *= dualScaleInverseNeg; sol._dual[r] -= _primalDualDiff.value(i); dualSize++; } else { // we do not check whether the dual value is nonzero, because it probably is; this gives us an // overestimation of the number of nonzeros in the dual solution dualSize++; } } } // update or recompute reduced cost values depending on which looks faster; adding one to the length of the // dual vector accounts for the objective function vector if(_primalDualDiff.size() < dualSize + 1) { _rationalLP->addDualActivity(_primalDualDiff, sol._redCost); #ifndef NDEBUG { VectorRational activity(_rationalLP->maxObj()); activity *= -1; _rationalLP->subDualActivity(sol._dual, activity); } #endif } else { // we assume that the objective function vector has less nonzeros than the reduced cost vector, and so multiplying // with -1 first and subtracting the dual activity should be faster than adding the dual activity and negating // afterwards _rationalLP->getObj(sol._redCost); _rationalLP->subDualActivity(sol._dual, sol._redCost); } const int numCorrectedDuals = _primalDualDiff.size(); if(numCorrectedPrimals + numCorrectedDuals > 0) { MSG_INFO2(spxout, spxout << "Corrected " << numCorrectedPrimals << " primal variables and " << numCorrectedDuals << " dual values.\n"); } } while(true); // correct basis status for restricted inequalities if(_hasBasis) { for(int r = numRowsRational() - 1; r >= 0; r--) { assert((lhsRational(r) == rhsRational(r)) == (_rowTypes[r] == RANGETYPE_FIXED)); if(_rowTypes[r] != RANGETYPE_FIXED && _basisStatusRows[r] == SPxSolverBase::FIXED) _basisStatusRows[r] = (maximizing == (sol._dual[r] < 0)) ? SPxSolverBase::ON_LOWER : SPxSolverBase::ON_UPPER; } } // compute objective function values assert(sol._isPrimalFeasible == sol._isDualFeasible); if(sol._isPrimalFeasible) { sol._objVal = sol._primal * _rationalLP->maxObj(); if(intParam(SoPlexBase::OBJSENSE) == SoPlexBase::OBJSENSE_MINIMIZE) sol._objVal *= -1; } // set objective coefficients for all rows to zero _solver.clearRowObjs(); // stop rational solving time _statistics->rationalTime->stop(); } /// performs iterative refinement on the auxiliary problem for testing unboundedness template void SoPlexBase::_performUnboundedIRStable( SolRational& sol, bool& hasUnboundedRay, bool& stoppedTime, bool& stoppedIter, bool& error) { bool primalFeasible; bool dualFeasible; bool infeasible; bool unbounded; // move objective function to constraints and adjust sides and bounds _transformUnbounded(); // invalidate solution sol.invalidate(); // remember current number of refinements int oldRefinements = _statistics->refinements; // perform iterative refinement _performOptIRStable(sol, false, false, 0, primalFeasible, dualFeasible, infeasible, unbounded, stoppedTime, stoppedIter, error); // update unbounded refinement counter _statistics->unbdRefinements += _statistics->refinements - oldRefinements; // stopped due to some limit if(stoppedTime || stoppedIter) { sol.invalidate(); hasUnboundedRay = false; error = false; } // the unbounded problem should always be solved to optimality else if(error || unbounded || infeasible || !primalFeasible || !dualFeasible) { sol.invalidate(); hasUnboundedRay = false; error = true; } else { const Rational& tau = sol._primal[numColsRational() - 1]; MSG_DEBUG(std::cout << "tau = " << tau << " (roughly " << tau.str() << ")\n"); assert(tau <= 1.0 + 2.0 * realParam(SoPlexBase::FEASTOL)); assert(tau >= -realParam(SoPlexBase::FEASTOL)); // because the right-hand side and all bounds (but tau's upper bound) are zero, tau should be approximately // zero if basic; otherwise at its upper bound 1 error = !(tau >= _rationalPosone || tau <= _rationalFeastol); assert(!error); hasUnboundedRay = (tau >= 1); } // restore problem _untransformUnbounded(sol, hasUnboundedRay); } /// performs iterative refinement on the auxiliary problem for testing feasibility template void SoPlexBase::_performFeasIRStable( SolRational& sol, bool& withDualFarkas, bool& stoppedTime, bool& stoppedIter, bool& error) { bool primalFeasible; bool dualFeasible; bool infeasible; bool unbounded; bool success = false; error = false; #if 0 // if the problem has been found to be infeasible and an approximate Farkas proof is available, we compute a // scaled unit box around the origin that provably contains no feasible solution; this currently only works for // equality form ///@todo check whether approximate Farkas proof can be used _computeInfeasBox(_solRational, false); ///@todo if approx Farkas proof is good enough then exit without doing any transformation #endif // remove objective function, shift, homogenize _transformFeasibility(); // invalidate solution sol.invalidate(); do { // remember current number of refinements int oldRefinements = _statistics->refinements; // perform iterative refinement _performOptIRStable(sol, false, false, 0, primalFeasible, dualFeasible, infeasible, unbounded, stoppedTime, stoppedIter, error); // update feasible refinement counter _statistics->feasRefinements += _statistics->refinements - oldRefinements; // stopped due to some limit if(stoppedTime || stoppedIter) { sol.invalidate(); withDualFarkas = false; error = false; } // the feasibility problem should always be solved to optimality else if(error || unbounded || infeasible || !primalFeasible || !dualFeasible) { sol.invalidate(); withDualFarkas = false; error = true; } // else we should have either a refined Farkas proof or an approximate feasible solution to the original else { const Rational& tau = sol._primal[numColsRational() - 1]; MSG_DEBUG(std::cout << "tau = " << tau << " (roughly " << tau.str() << ")\n"); assert(tau >= -realParam(SoPlexBase::FEASTOL)); assert(tau <= 1.0 + realParam(SoPlexBase::FEASTOL)); error = (tau < -_rationalFeastol || tau > _rationalPosone + _rationalFeastol); withDualFarkas = (tau < _rationalPosone); if(withDualFarkas) { _solRational._hasDualFarkas = true; _solRational._dualFarkas = _solRational._dual; #if 0 // check if we can compute sufficiently large Farkas box _computeInfeasBox(_solRational, true); #endif if(true) //@todo check if computeInfeasBox found a sufficient box { success = true; sol._isPrimalFeasible = false; } } else { sol._isDualFeasible = false; success = true; //successfully found approximate feasible solution } } } while(!error && !success && !(stoppedTime || stoppedIter)); // restore problem _untransformFeasibility(sol, withDualFarkas); } /// reduces matrix coefficient in absolute value by the lifting procedure of Thiele et al. 2013 template void SoPlexBase::_lift() { MSG_DEBUG(std::cout << "Reducing matrix coefficients by lifting.\n"); // start timing _statistics->transformTime->start(); MSG_DEBUG(_realLP->writeFileLPBase("beforeLift.lp", 0, 0, 0)); // remember unlifted state _beforeLiftCols = numColsRational(); _beforeLiftRows = numRowsRational(); // allocate vector memory DSVectorRational colVector; SVectorRational::Element liftingRowMem[2]; SVectorRational liftingRowVector(2, liftingRowMem); // search each column for large nonzeros entries // @todo: rethink about the static_cast TODO const Rational maxValue = static_cast(realParam(SoPlexBase::LIFTMAXVAL)); for(int i = 0; i < numColsRational(); i++) { MSG_DEBUG(std::cout << "in lifting: examining column " << i << "\n"); // get column vector colVector = colVectorRational(i); bool addedLiftingRow = false; int liftingColumnIndex = -1; // go through nonzero entries of the column for(int k = colVector.size() - 1; k >= 0; k--) { const Rational& value = colVector.value(k); if(spxAbs(value) > maxValue) { MSG_DEBUG(std::cout << " --> nonzero " << k << " has value " << value.str() << " in row " << colVector.index(k) << "\n"); // add new column equal to maxValue times original column if(!addedLiftingRow) { MSG_DEBUG(std::cout << " --> adding lifting row\n"); assert(liftingRowVector.size() == 0); liftingColumnIndex = numColsRational(); liftingRowVector.add(i, maxValue); liftingRowVector.add(liftingColumnIndex, -1); _rationalLP->addRow(LPRowRational(0, liftingRowVector, 0)); _realLP->addRow(LPRowBase(0.0, DSVectorBase(liftingRowVector), 0.0)); assert(liftingColumnIndex == numColsRational() - 1); assert(liftingColumnIndex == numCols() - 1); _rationalLP->changeBounds(liftingColumnIndex, _rationalNegInfty, _rationalPosInfty); _realLP->changeBounds(liftingColumnIndex, -realParam(SoPlexBase::INFTY), realParam(SoPlexBase::INFTY)); liftingRowVector.clear(); addedLiftingRow = true; } // get row index int rowIndex = colVector.index(k); assert(rowIndex >= 0); assert(rowIndex < _beforeLiftRows); assert(liftingColumnIndex == numColsRational() - 1); MSG_DEBUG(std::cout << " --> changing matrix\n"); // remove nonzero from original column _rationalLP->changeElement(rowIndex, i, 0); _realLP->changeElement(rowIndex, i, 0.0); // add nonzero divided by maxValue to new column Rational newValue(value); newValue /= maxValue; _rationalLP->changeElement(rowIndex, liftingColumnIndex, newValue); _realLP->changeElement(rowIndex, liftingColumnIndex, R(newValue)); } } } // search each column for small nonzeros entries const Rational minValue = Rational(realParam(SoPlexBase::LIFTMINVAL)); for(int i = 0; i < numColsRational(); i++) { MSG_DEBUG(std::cout << "in lifting: examining column " << i << "\n"); // get column vector colVector = colVectorRational(i); bool addedLiftingRow = false; int liftingColumnIndex = -1; // go through nonzero entries of the column for(int k = colVector.size() - 1; k >= 0; k--) { const Rational& value = colVector.value(k); if(spxAbs(value) < minValue) { MSG_DEBUG(std::cout << " --> nonzero " << k << " has value " << value.str() << " in row " << colVector.index(k) << "\n"); // add new column equal to maxValue times original column if(!addedLiftingRow) { MSG_DEBUG(std::cout << " --> adding lifting row\n"); assert(liftingRowVector.size() == 0); liftingColumnIndex = numColsRational(); liftingRowVector.add(i, minValue); liftingRowVector.add(liftingColumnIndex, -1); _rationalLP->addRow(LPRowRational(0, liftingRowVector, 0)); _realLP->addRow(LPRowBase(0.0, DSVectorBase(liftingRowVector), 0.0)); assert(liftingColumnIndex == numColsRational() - 1); assert(liftingColumnIndex == numCols() - 1); _rationalLP->changeBounds(liftingColumnIndex, _rationalNegInfty, _rationalPosInfty); _realLP->changeBounds(liftingColumnIndex, -realParam(SoPlexBase::INFTY), realParam(SoPlexBase::INFTY)); liftingRowVector.clear(); addedLiftingRow = true; } // get row index int rowIndex = colVector.index(k); assert(rowIndex >= 0); assert(rowIndex < _beforeLiftRows); assert(liftingColumnIndex == numColsRational() - 1); MSG_DEBUG(std::cout << " --> changing matrix\n"); // remove nonzero from original column _rationalLP->changeElement(rowIndex, i, 0); _realLP->changeElement(rowIndex, i, 0.0); // add nonzero divided by maxValue to new column Rational newValue(value); newValue /= minValue; _rationalLP->changeElement(rowIndex, liftingColumnIndex, newValue); _realLP->changeElement(rowIndex, liftingColumnIndex, R(newValue)); } } } // adjust basis if(_hasBasis) { assert(numColsRational() >= _beforeLiftCols); assert(numRowsRational() >= _beforeLiftRows); _basisStatusCols.append(numColsRational() - _beforeLiftCols, SPxSolverBase::BASIC); _basisStatusRows.append(numRowsRational() - _beforeLiftRows, SPxSolverBase::FIXED); _rationalLUSolver.clear(); } MSG_DEBUG(_realLP->writeFileLPBase("afterLift.lp", 0, 0, 0)); // stop timing _statistics->transformTime->stop(); if(numColsRational() > _beforeLiftCols || numRowsRational() > _beforeLiftRows) { MSG_INFO1(spxout, spxout << "Added " << numColsRational() - _beforeLiftCols << " columns and " << numRowsRational() - _beforeLiftRows << " rows to reduce large matrix coefficients\n."); } } /// undoes lifting template void SoPlexBase::_project(SolRational& sol) { // start timing _statistics->transformTime->start(); // print LP if in debug mode MSG_DEBUG(_realLP->writeFileLPBase("beforeProject.lp", 0, 0, 0)); assert(numColsRational() >= _beforeLiftCols); assert(numRowsRational() >= _beforeLiftRows); // shrink rational LP to original size _rationalLP->removeColRange(_beforeLiftCols, numColsRational() - 1); _rationalLP->removeRowRange(_beforeLiftRows, numRowsRational() - 1); // shrink real LP to original size _realLP->removeColRange(_beforeLiftCols, numColsReal() - 1); _realLP->removeRowRange(_beforeLiftRows, numRowsReal() - 1); // adjust solution if(sol.isPrimalFeasible()) { sol._primal.reDim(_beforeLiftCols); sol._slacks.reDim(_beforeLiftRows); } if(sol.hasPrimalRay()) { sol._primalRay.reDim(_beforeLiftCols); } ///@todo if we know the mapping between original and lifting columns, we simply need to add the reduced cost of /// the lifting column to the reduced cost of the original column; this is not implemented now, because for /// optimal solutions the reduced costs of the lifting columns are zero const Rational maxValue = Rational(realParam(SoPlexBase::LIFTMAXVAL)); for(int i = _beforeLiftCols; i < numColsRational() && sol._isDualFeasible; i++) { if(spxAbs(Rational(maxValue * sol._redCost[i])) > _rationalOpttol) { MSG_INFO1(spxout, spxout << "Warning: lost dual solution during project phase.\n"); sol._isDualFeasible = false; } } if(sol.isDualFeasible()) { sol._redCost.reDim(_beforeLiftCols); sol._dual.reDim(_beforeLiftRows); } if(sol.hasDualFarkas()) { sol._dualFarkas.reDim(_beforeLiftRows); } // adjust basis for(int i = _beforeLiftCols; i < numColsRational() && _hasBasis; i++) { if(_basisStatusCols[i] != SPxSolverBase::BASIC) { MSG_INFO1(spxout, spxout << "Warning: lost basis during project phase because of nonbasic lifting column.\n"); _hasBasis = false; _rationalLUSolver.clear(); } } for(int i = _beforeLiftRows; i < numRowsRational() && _hasBasis; i++) { if(_basisStatusRows[i] == SPxSolverBase::BASIC) { MSG_INFO1(spxout, spxout << "Warning: lost basis during project phase because of basic lifting row.\n"); _hasBasis = false; _rationalLUSolver.clear(); } } if(_hasBasis) { _basisStatusCols.reSize(_beforeLiftCols); _basisStatusRows.reSize(_beforeLiftRows); _rationalLUSolver.clear(); } // print LP if in debug mode MSG_DEBUG(_realLP->writeFileLPBase("afterProject.lp", 0, 0, 0)); // stop timing _statistics->transformTime->stop(); } /// stores objective, bounds, and sides of real LP template void SoPlexBase::_storeLPReal() { #ifndef SOPLEX_MANUAL_ALT if(intParam(SoPlexBase::SYNCMODE) == SYNCMODE_MANUAL) { _manualRealLP = *_realLP; return; } #endif _manualLower = _realLP->lower(); _manualUpper = _realLP->upper(); _manualLhs = _realLP->lhs(); _manualRhs = _realLP->rhs(); _manualObj.reDim(_realLP->nCols()); _realLP->getObj(_manualObj); } /// restores objective, bounds, and sides of real LP template void SoPlexBase::_restoreLPReal() { if(intParam(SoPlexBase::SYNCMODE) == SYNCMODE_MANUAL) { #ifndef SOPLEX_MANUAL_ALT _solver.loadLP(_manualRealLP); #else _realLP->changeLower(_manualLower); _realLP->changeUpper(_manualUpper); _realLP->changeLhs(_manualLhs); _realLP->changeRhs(_manualRhs); _realLP->changeObj(_manualObj); #endif if(_hasBasis) { // in manual sync mode, if the right-hand side of an equality constraint is not floating-point // representable, the user might have constructed the constraint in the real LP by rounding down the // left-hand side and rounding up the right-hand side; if the basis status is fixed, we need to adjust it for(int i = 0; i < _solver.nRows(); i++) { if(_basisStatusRows[i] == SPxSolverBase::FIXED && _solver.lhs(i) != _solver.rhs(i)) { assert(_solver.rhs(i) == spxNextafter(_solver.lhs(i), R(infinity))); if(_hasSolRational && _solRational.isDualFeasible() && ((intParam(SoPlexBase::OBJSENSE) == SoPlexBase::OBJSENSE_MAXIMIZE && _solRational._dual[i] > 0) || (intParam(SoPlexBase::OBJSENSE) == SoPlexBase::OBJSENSE_MINIMIZE && _solRational._dual[i] < 0))) { _basisStatusRows[i] = SPxSolverBase::ON_UPPER; } else { _basisStatusRows[i] = SPxSolverBase::ON_LOWER; } } } _solver.setBasis(_basisStatusRows.get_const_ptr(), _basisStatusCols.get_const_ptr()); _hasBasis = (_solver.basis().status() > SPxBasisBase::NO_PROBLEM); } } else { _realLP->changeLower(_manualLower); _realLP->changeUpper(_manualUpper); _realLP->changeLhs(_manualLhs); _realLP->changeRhs(_manualRhs); _realLP->changeObj(_manualObj); } } /// introduces slack variables to transform inequality constraints into equations for both rational and real LP, /// which should be in sync template void SoPlexBase::_transformEquality() { MSG_DEBUG(std::cout << "Transforming rows to equation form.\n"); // start timing _statistics->transformTime->start(); MSG_DEBUG(_realLP->writeFileLPBase("beforeTransEqu.lp", 0, 0, 0)); // clear array of slack columns _slackCols.clear(); // add artificial slack variables to convert inequality to equality constraints for(int i = 0; i < numRowsRational(); i++) { assert((lhsRational(i) == rhsRational(i)) == (_rowTypes[i] == RANGETYPE_FIXED)); if(_rowTypes[i] != RANGETYPE_FIXED) { _slackCols.add(_rationalZero, -rhsRational(i), *_unitVectorRational(i), -lhsRational(i)); if(_rationalLP->lhs(i) != 0) _rationalLP->changeLhs(i, _rationalZero); if(_rationalLP->rhs(i) != 0) _rationalLP->changeRhs(i, _rationalZero); assert(_rationalLP->lhs(i) == 0); assert(_rationalLP->rhs(i) == 0); _realLP->changeRange(i, R(0.0), R(0.0)); _colTypes.append(_switchRangeType(_rowTypes[i])); _rowTypes[i] = RANGETYPE_FIXED; } } _rationalLP->addCols(_slackCols); _realLP->addCols(_slackCols); // adjust basis if(_hasBasis) { for(int i = 0; i < _slackCols.num(); i++) { int row = _slackCols.colVector(i).index(0); assert(row >= 0); assert(row < numRowsRational()); switch(_basisStatusRows[row]) { case SPxSolverBase::ON_LOWER: _basisStatusCols.append(SPxSolverBase::ON_UPPER); break; case SPxSolverBase::ON_UPPER: _basisStatusCols.append(SPxSolverBase::ON_LOWER); break; case SPxSolverBase::BASIC: case SPxSolverBase::FIXED: default: _basisStatusCols.append(_basisStatusRows[row]); break; } _basisStatusRows[row] = SPxSolverBase::FIXED; } _rationalLUSolver.clear(); } MSG_DEBUG(_realLP->writeFileLPBase("afterTransEqu.lp", 0, 0, 0)); // stop timing _statistics->transformTime->stop(); if(_slackCols.num() > 0) { MSG_INFO1(spxout, spxout << "Added " << _slackCols.num() << " slack columns to transform rows to equality form.\n"); } } /// restores original problem template void SoPlexBase::_untransformEquality(SolRational& sol) { // start timing _statistics->transformTime->start(); // print LP if in debug mode MSG_DEBUG(_realLP->writeFileLPBase("beforeUntransEqu.lp", 0, 0, 0)); int numCols = numColsRational(); int numOrigCols = numColsRational() - _slackCols.num(); // adjust solution if(sol.isPrimalFeasible()) { for(int i = 0; i < _slackCols.num(); i++) { int col = numOrigCols + i; int row = _slackCols.colVector(i).index(0); assert(row >= 0); assert(row < numRowsRational()); sol._slacks[row] -= sol._primal[col]; } sol._primal.reDim(numOrigCols); } if(sol.hasPrimalRay()) { sol._primalRay.reDim(numOrigCols); } // adjust basis if(_hasBasis) { for(int i = 0; i < _slackCols.num(); i++) { int col = numOrigCols + i; int row = _slackCols.colVector(i).index(0); assert(row >= 0); assert(row < numRowsRational()); assert(_basisStatusRows[row] != SPxSolverBase::UNDEFINED); assert(_basisStatusRows[row] != SPxSolverBase::ZERO || lhsRational(row) == 0); assert(_basisStatusRows[row] != SPxSolverBase::ZERO || rhsRational(row) == 0); assert(_basisStatusRows[row] != SPxSolverBase::BASIC || _basisStatusCols[col] != SPxSolverBase::BASIC); MSG_DEBUG(std::cout << "slack column " << col << " for row " << row << ": col status=" << _basisStatusCols[col] << ", row status=" << _basisStatusRows[row] << ", redcost=" << sol._redCost[col].str() << ", dual=" << sol._dual[row].str() << "\n"); if(_basisStatusRows[row] != SPxSolverBase::BASIC) { switch(_basisStatusCols[col]) { case SPxSolverBase::ON_LOWER: _basisStatusRows[row] = SPxSolverBase::ON_UPPER; break; case SPxSolverBase::ON_UPPER: _basisStatusRows[row] = SPxSolverBase::ON_LOWER; break; case SPxSolverBase::BASIC: case SPxSolverBase::FIXED: default: _basisStatusRows[row] = _basisStatusCols[col]; break; } } } _basisStatusCols.reSize(numOrigCols); if(_slackCols.num() > 0) _rationalLUSolver.clear(); } // not earlier because of debug message if(sol.isDualFeasible()) { sol._redCost.reDim(numOrigCols); } // restore sides and remove slack columns for(int i = 0; i < _slackCols.num(); i++) { int col = numOrigCols + i; int row = _slackCols.colVector(i).index(0); if(upperRational(col) != 0) _rationalLP->changeLhs(row, -upperRational(col)); if(lowerRational(col) != 0) _rationalLP->changeRhs(row, -lowerRational(col)); assert(_rationalLP->lhs(row) == -upperRational(col)); assert(_rationalLP->rhs(row) == -lowerRational(col)); _rowTypes[row] = _switchRangeType(_colTypes[col]); } _rationalLP->removeColRange(numOrigCols, numCols - 1); _realLP->removeColRange(numOrigCols, numCols - 1); _colTypes.reSize(numOrigCols); // objective, bounds, and sides of real LP are restored only after _solveRational() // print LP if in debug mode MSG_DEBUG(_realLP->writeFileLPBase("afterUntransEqu.lp", 0, 0, 0)); // stop timing _statistics->transformTime->stop(); } /// transforms LP to unboundedness problem by moving the objective function to the constraints, changing right-hand /// side and bounds to zero, and adding an auxiliary variable for the decrease in the objective function template void SoPlexBase::_transformUnbounded() { MSG_INFO1(spxout, spxout << "Setting up LP to compute primal unbounded ray.\n"); // start timing _statistics->transformTime->start(); // print LP if in debug mode MSG_DEBUG(_realLP->writeFileLPBase("beforeTransUnbounded.lp", 0, 0, 0)); // store bounds _unboundedLower.reDim(numColsRational()); _unboundedUpper.reDim(numColsRational()); for(int c = numColsRational() - 1; c >= 0; c--) { if(_lowerFinite(_colTypes[c])) _unboundedLower[c] = lowerRational(c); if(_upperFinite(_colTypes[c])) _unboundedUpper[c] = upperRational(c); } // store sides _unboundedLhs.reDim(numRowsRational()); _unboundedRhs.reDim(numRowsRational()); for(int r = numRowsRational() - 1; r >= 0; r--) { if(_lowerFinite(_rowTypes[r])) _unboundedLhs[r] = lhsRational(r); if(_upperFinite(_rowTypes[r])) _unboundedRhs[r] = rhsRational(r); } // make right-hand side zero for(int r = numRowsRational() - 1; r >= 0; r--) { assert((lhsRational(r) > _rationalNegInfty) == _lowerFinite(_rowTypes[r])); if(_lowerFinite(_rowTypes[r])) { _rationalLP->changeLhs(r, Rational(0)); _realLP->changeLhs(r, R(0.0)); } else if(_realLP->lhs(r) > -realParam(SoPlexBase::INFTY)) _realLP->changeLhs(r, -realParam(SoPlexBase::INFTY)); assert((rhsRational(r) < _rationalPosInfty) == _upperFinite(_rowTypes[r])); if(_upperFinite(_rowTypes[r])) { _rationalLP->changeRhs(r, Rational(0)); _realLP->changeRhs(r, R(0.0)); } else if(_realLP->rhs(r) < realParam(SoPlexBase::INFTY)) _realLP->changeRhs(r, realParam(SoPlexBase::INFTY)); } // transform objective function to constraint and add auxiliary variable int numOrigCols = numColsRational(); DSVectorRational obj(numOrigCols + 1); ///@todo implement this without copying the objective function obj = _rationalLP->maxObj(); obj.add(numOrigCols, -1); _rationalLP->addRow(LPRowRational(0, obj, 0)); _realLP->addRow(LPRowBase(0, DSVectorBase(obj), 0)); _rowTypes.append(RANGETYPE_FIXED); assert(numColsRational() == numOrigCols + 1); // set objective coefficient and bounds for auxiliary variable _rationalLP->changeMaxObj(numOrigCols, Rational(1)); _realLP->changeMaxObj(numOrigCols, R(1.0)); _rationalLP->changeBounds(numOrigCols, _rationalNegInfty, 1); _realLP->changeBounds(numOrigCols, -realParam(SoPlexBase::INFTY), 1.0); _colTypes.append(RANGETYPE_UPPER); // set objective coefficients to zero and adjust bounds for problem variables for(int c = numColsRational() - 2; c >= 0; c--) { _rationalLP->changeObj(c, Rational(0)); _realLP->changeObj(c, R(0.0)); assert((lowerRational(c) > _rationalNegInfty) == _lowerFinite(_colTypes[c])); if(_lowerFinite(_colTypes[c])) { _rationalLP->changeLower(c, Rational(0)); _realLP->changeLower(c, R(0.0)); } else if(_realLP->lower(c) > -realParam(SoPlexBase::INFTY)) _realLP->changeLower(c, -realParam(SoPlexBase::INFTY)); assert((upperRational(c) < _rationalPosInfty) == _upperFinite(_colTypes[c])); if(_upperFinite(_colTypes[c])) { _rationalLP->changeUpper(c, Rational(0)); _realLP->changeUpper(c, R(0.0)); } else if(_realLP->upper(c) < realParam(SoPlexBase::INFTY)) _realLP->changeUpper(c, realParam(SoPlexBase::INFTY)); } // adjust basis if(_hasBasis) { _basisStatusCols.append(SPxSolverBase::ON_UPPER); _basisStatusRows.append(SPxSolverBase::BASIC); _rationalLUSolver.clear(); } // print LP if in debug mode MSG_DEBUG(_realLP->writeFileLPBase("afterTransUnbounded.lp", 0, 0, 0)); // stop timing _statistics->transformTime->stop(); } /// undoes transformation to unboundedness problem template void SoPlexBase::_untransformUnbounded(SolRational& sol, bool unbounded) { // start timing _statistics->transformTime->start(); // print LP if in debug mode MSG_DEBUG(_realLP->writeFileLPBase("beforeUntransUnbounded.lp", 0, 0, 0)); int numOrigCols = numColsRational() - 1; int numOrigRows = numRowsRational() - 1; const Rational& tau = sol._primal[numOrigCols]; // adjust solution and basis if(unbounded) { assert(tau >= _rationalPosone); sol._isPrimalFeasible = false; sol._hasPrimalRay = true; sol._isDualFeasible = false; sol._hasDualFarkas = false; if(tau != 1) sol._primal /= tau; sol._primalRay = sol._primal; sol._primalRay.reDim(numOrigCols); _hasBasis = (_basisStatusCols[numOrigCols] != SPxSolverBase::BASIC && _basisStatusRows[numOrigRows] == SPxSolverBase::BASIC); _basisStatusCols.reSize(numOrigCols); _basisStatusRows.reSize(numOrigRows); } else if(boolParam(SoPlexBase::TESTDUALINF) && tau < _rationalFeastol) { const Rational& alpha = sol._dual[numOrigRows]; assert(sol._isDualFeasible); assert(alpha <= _rationalFeastol - _rationalPosone); sol._isPrimalFeasible = false; sol._hasPrimalRay = false; sol._hasDualFarkas = false; if(alpha != -1) { sol._dual /= -alpha; sol._redCost /= -alpha; } sol._dual.reDim(numOrigRows); sol._redCost.reDim(numOrigCols); } else { sol.invalidate(); _hasBasis = false; _basisStatusCols.reSize(numOrigCols); _basisStatusCols.reSize(numOrigRows); } // recover objective function const SVectorRational& objRowVector = _rationalLP->rowVector(numOrigRows); for(int i = objRowVector.size() - 1; i >= 0; i--) { _rationalLP->changeMaxObj(objRowVector.index(i), objRowVector.value(i)); _realLP->changeMaxObj(objRowVector.index(i), R(objRowVector.value(i))); } // remove objective function constraint and auxiliary variable _rationalLP->removeRow(numOrigRows); _realLP->removeRow(numOrigRows); _rowTypes.reSize(numOrigRows); _rationalLP->removeCol(numOrigCols); _realLP->removeCol(numOrigCols); _colTypes.reSize(numOrigCols); // restore objective, sides and bounds for(int r = numRowsRational() - 1; r >= 0; r--) { if(_lowerFinite(_rowTypes[r])) { _rationalLP->changeLhs(r, _unboundedLhs[r]); _realLP->changeLhs(r, R(_unboundedLhs[r])); } if(_upperFinite(_rowTypes[r])) { _rationalLP->changeRhs(r, _unboundedRhs[r]); _realLP->changeRhs(r, R(_unboundedRhs[r])); } assert((lhsRational(r) > _rationalNegInfty) == _lowerFinite(_rowTypes[r])); assert((rhsRational(r) < _rationalPosInfty) == _upperFinite(_rowTypes[r])); assert((lhsReal(r) > -realParam(SoPlexBase::INFTY)) == _lowerFinite(_rowTypes[r])); assert((rhsReal(r) < realParam(SoPlexBase::INFTY)) == _upperFinite(_rowTypes[r])); } for(int c = numColsRational() - 1; c >= 0; c--) { if(_lowerFinite(_colTypes[c])) { _rationalLP->changeLower(c, _unboundedLower[c]); _realLP->changeLower(c, R(_unboundedLower[c])); } if(_upperFinite(_colTypes[c])) { _rationalLP->changeUpper(c, _unboundedUpper[c]); _realLP->changeUpper(c, R(_unboundedUpper[c])); } assert((lowerRational(c) > _rationalNegInfty) == _lowerFinite(_colTypes[c])); assert((upperRational(c) < _rationalPosInfty) == _upperFinite(_colTypes[c])); assert((lowerReal(c) > -realParam(SoPlexBase::INFTY)) == _lowerFinite(_colTypes[c])); assert((upperReal(c) < realParam(SoPlexBase::INFTY)) == _upperFinite(_colTypes[c])); } // invalidate rational basis factorization _rationalLUSolver.clear(); // print LP if in debug mode MSG_DEBUG(_realLP->writeFileLPBase("afterUntransUnbounded.lp", 0, 0, 0)); // stop timing _statistics->transformTime->stop(); } /// store basis template void SoPlexBase::_storeBasis() { assert(!_storedBasis); if(_hasBasis) { _storedBasis = true; _storedBasisStatusCols = _basisStatusCols; _storedBasisStatusRows = _basisStatusRows; } else _storedBasis = false; } /// restore basis template void SoPlexBase::_restoreBasis() { if(_storedBasis) { _hasBasis = true; _basisStatusCols = _storedBasisStatusCols; _basisStatusRows = _storedBasisStatusRows; _storedBasis = false; } } /// transforms LP to feasibility problem by removing the objective function, shifting variables, and homogenizing the /// right-hand side template void SoPlexBase::_transformFeasibility() { MSG_INFO1(spxout, spxout << "Setting up LP to test for feasibility.\n"); // start timing _statistics->transformTime->start(); // print LP if in debug mode MSG_DEBUG(_realLP->writeFileLPBase("beforeTransFeas.lp", 0, 0, 0)); // store objective function _feasObj.reDim(numColsRational()); for(int c = numColsRational() - 1; c >= 0; c--) _feasObj[c] = _rationalLP->maxObj(c); // store bounds _feasLower.reDim(numColsRational()); _feasUpper.reDim(numColsRational()); for(int c = numColsRational() - 1; c >= 0; c--) { if(_lowerFinite(_colTypes[c])) _feasLower[c] = lowerRational(c); if(_upperFinite(_colTypes[c])) _feasUpper[c] = upperRational(c); } // store sides _feasLhs.reDim(numRowsRational()); _feasRhs.reDim(numRowsRational()); for(int r = numRowsRational() - 1; r >= 0; r--) { if(_lowerFinite(_rowTypes[r])) _feasLhs[r] = lhsRational(r); if(_upperFinite(_rowTypes[r])) _feasRhs[r] = rhsRational(r); } // set objective coefficients to zero; shift primal space such as to guarantee that the zero solution is within // the bounds Rational shiftValue; Rational shiftValue2; for(int c = numColsRational() - 1; c >= 0; c--) { _rationalLP->changeMaxObj(c, Rational(0)); _realLP->changeMaxObj(c, R(0.0)); if(lowerRational(c) > 0) { const SVectorRational& colVector = colVectorRational(c); for(int i = 0; i < colVector.size(); i++) { shiftValue = colVector.value(i); shiftValue *= lowerRational(c); int r = colVector.index(i); assert((lhsRational(r) > _rationalNegInfty) == _lowerFinite(_rowTypes[r])); assert((rhsRational(r) < _rationalPosInfty) == _upperFinite(_rowTypes[r])); if(_lowerFinite(_rowTypes[r]) && _upperFinite(_rowTypes[r])) { shiftValue2 = lhsRational(r); shiftValue2 -= shiftValue; _rationalLP->changeLhs(r, shiftValue2); _realLP->changeLhs(r, R(shiftValue2)); shiftValue -= rhsRational(r); shiftValue *= -1; _rationalLP->changeRhs(r, shiftValue); _realLP->changeRhs(r, R(shiftValue)); } else if(_lowerFinite(_rowTypes[r])) { shiftValue -= lhsRational(r); shiftValue *= -1; _rationalLP->changeLhs(r, shiftValue); _realLP->changeLhs(r, R(shiftValue)); } else if(_upperFinite(_rowTypes[r])) { shiftValue -= rhsRational(r); shiftValue *= -1; _rationalLP->changeRhs(r, shiftValue); _realLP->changeRhs(r, R(shiftValue)); } } assert((upperRational(c) < _rationalPosInfty) == _upperFinite(_colTypes[c])); if(_upperFinite(_colTypes[c])) { _rationalLP->changeBounds(c, 0, upperRational(c) - lowerRational(c)); _realLP->changeBounds(c, 0.0, R(upperRational(c))); } else if(_realLP->upper(c) < realParam(SoPlexBase::INFTY)) { _rationalLP->changeLower(c, Rational(0)); _realLP->changeBounds(c, 0.0, realParam(SoPlexBase::INFTY)); } else { _rationalLP->changeLower(c, Rational(0)); _realLP->changeLower(c, R(0.0)); } } else if(upperRational(c) < 0) { const SVectorRational& colVector = colVectorRational(c); for(int i = 0; i < colVector.size(); i++) { shiftValue = colVector.value(i); shiftValue *= upperRational(c); int r = colVector.index(i); assert((lhsRational(r) > _rationalNegInfty) == _lowerFinite(_rowTypes[r])); assert((rhsRational(r) < _rationalPosInfty) == _upperFinite(_rowTypes[r])); if(_lowerFinite(_rowTypes[r]) && _upperFinite(_rowTypes[r])) { shiftValue2 = lhsRational(r); shiftValue2 -= shiftValue; _rationalLP->changeLhs(r, shiftValue2); _realLP->changeLhs(r, R(shiftValue2)); shiftValue -= rhsRational(r); shiftValue *= -1; _rationalLP->changeRhs(r, shiftValue); _realLP->changeRhs(r, R(shiftValue)); } else if(_lowerFinite(_rowTypes[r])) { shiftValue -= lhsRational(r); shiftValue *= -1; _rationalLP->changeLhs(r, shiftValue); _realLP->changeLhs(r, R(shiftValue)); } else if(_upperFinite(_rowTypes[r])) { shiftValue -= rhsRational(r); shiftValue *= -1; _rationalLP->changeRhs(r, shiftValue); _realLP->changeRhs(r, R(shiftValue)); } } assert((lowerRational(c) > _rationalNegInfty) == _lowerFinite(_colTypes[c])); if(_lowerFinite(_colTypes[c])) { _rationalLP->changeBounds(c, lowerRational(c) - upperRational(c), 0); _realLP->changeBounds(c, R(lowerRational(c)), 0.0); } else if(_realLP->lower(c) > -realParam(SoPlexBase::INFTY)) { _rationalLP->changeUpper(c, Rational(0)); _realLP->changeBounds(c, -realParam(SoPlexBase::INFTY), 0.0); } else { _rationalLP->changeUpper(c, Rational(0)); _realLP->changeUpper(c, R(0.0)); } } else { if(_lowerFinite(_colTypes[c])) _realLP->changeLower(c, R(lowerRational(c))); else if(_realLP->lower(c) > -realParam(SoPlexBase::INFTY)) _realLP->changeLower(c, -realParam(SoPlexBase::INFTY)); if(_upperFinite(_colTypes[c])) _realLP->changeUpper(c, R(upperRational(c))); else if(_realLP->upper(c) < realParam(SoPlexBase::INFTY)) _realLP->changeUpper(c, realParam(SoPlexBase::INFTY)); } assert(lowerReal(c) <= upperReal(c)); } // homogenize sides _tauColVector.clear(); for(int r = numRowsRational() - 1; r >= 0; r--) { if(lhsRational(r) > 0) { _tauColVector.add(r, lhsRational(r)); assert((rhsRational(r) < _rationalPosInfty) == _upperFinite(_rowTypes[r])); if(_upperFinite(_rowTypes[r])) { _rationalLP->changeRange(r, 0, rhsRational(r) - lhsRational(r)); _realLP->changeRange(r, 0.0, R(rhsRational(r))); } else { _rationalLP->changeLhs(r, Rational(0)); _realLP->changeLhs(r, R(0.0)); if(_realLP->rhs(r) < realParam(SoPlexBase::INFTY)) _realLP->changeRhs(r, realParam(SoPlexBase::INFTY)); } } else if(rhsRational(r) < 0) { _tauColVector.add(r, rhsRational(r)); assert((lhsRational(r) > _rationalNegInfty) == _lowerFinite(_rowTypes[r])); if(_lowerFinite(_rowTypes[r])) { _rationalLP->changeRange(r, lhsRational(r) - rhsRational(r), 0); _realLP->changeRange(r, R(lhsRational(r)), 0.0); } else { _rationalLP->changeRhs(r, Rational(0)); _realLP->changeRhs(r, R(0.0)); if(_realLP->lhs(r) > -realParam(SoPlexBase::INFTY)) _realLP->changeLhs(r, -realParam(SoPlexBase::INFTY)); } } else { if(_lowerFinite(_rowTypes[r])) _realLP->changeLhs(r, R(lhsRational(r))); else if(_realLP->lhs(r) > -realParam(SoPlexBase::INFTY)) _realLP->changeLhs(r, -realParam(SoPlexBase::INFTY)); if(_upperFinite(_rowTypes[r])) _realLP->changeRhs(r, R(rhsRational(r))); else if(_realLP->rhs(r) < realParam(SoPlexBase::INFTY)) _realLP->changeRhs(r, realParam(SoPlexBase::INFTY)); } assert(rhsReal(r) <= rhsReal(r)); } ///@todo exploit this case by returning without LP solving if(_tauColVector.size() == 0) { MSG_INFO3(spxout, spxout << "LP is trivially feasible.\n"); } // add artificial column SPxColId id; _tauColVector *= -1; _rationalLP->addCol(id, LPColRational((intParam(SoPlexBase::OBJSENSE) == SoPlexBase::OBJSENSE_MAXIMIZE ? _rationalPosone : _rationalNegone), _tauColVector, 1, 0)); _realLP->addCol(id, LPColBase((intParam(SoPlexBase::OBJSENSE) == SoPlexBase::OBJSENSE_MAXIMIZE ? 1.0 : -1.0), DSVectorBase(_tauColVector), 1.0, 0.0)); _colTypes.append(RANGETYPE_BOXED); // adjust basis if(_hasBasis) { _basisStatusCols.append(SPxSolverBase::ON_UPPER); } // invalidate rational basis factorization _rationalLUSolver.clear(); // print LP if in debug mode MSG_DEBUG(_realLP->writeFileLPBase("afterTransFeas.lp", 0, 0, 0)); // stop timing _statistics->transformTime->stop(); } /// undoes transformation to feasibility problem template void SoPlexBase::_untransformFeasibility(SolRational& sol, bool infeasible) { // start timing _statistics->transformTime->start(); // print LP if in debug mode MSG_DEBUG(_realLP->writeFileLPBase("beforeUntransFeas.lp", 0, 0, 0)); int numOrigCols = numColsRational() - 1; // adjust solution and basis if(infeasible) { assert(sol._isDualFeasible); assert(sol._primal[numOrigCols] < 1); sol._isPrimalFeasible = false; sol._hasPrimalRay = false; sol._isDualFeasible = false; sol._hasDualFarkas = true; sol._dualFarkas = sol._dual; _hasBasis = false; _basisStatusCols.reSize(numOrigCols); } else if(sol._isPrimalFeasible) { assert(sol._primal[numOrigCols] >= 1); sol._hasPrimalRay = false; sol._isDualFeasible = false; sol._hasDualFarkas = false; if(sol._primal[numOrigCols] != 1) { sol._slacks /= sol._primal[numOrigCols]; for(int i = 0; i < numOrigCols; i++) sol._primal[i] /= sol._primal[numOrigCols]; sol._primal[numOrigCols] = 1; } sol._primal.reDim(numOrigCols); sol._slacks -= _rationalLP->colVector(numOrigCols); _hasBasis = (_basisStatusCols[numOrigCols] != SPxSolverBase::BASIC); _basisStatusCols.reSize(numOrigCols); } else { _hasBasis = false; _basisStatusCols.reSize(numOrigCols); } // restore right-hand side for(int r = numRowsRational() - 1; r >= 0; r--) { assert(rhsRational(r) >= _rationalPosInfty || lhsRational(r) <= _rationalNegInfty || _feasLhs[r] - lhsRational(r) == _feasRhs[r] - rhsRational(r)); if(_lowerFinite(_rowTypes[r])) { _rationalLP->changeLhs(r, _feasLhs[r]); _realLP->changeLhs(r, R(_feasLhs[r])); } else if(_realLP->lhs(r) > -realParam(SoPlexBase::INFTY)) _realLP->changeLhs(r, -realParam(SoPlexBase::INFTY)); assert(_lowerFinite(_rowTypes[r]) == (lhsRational(r) > _rationalNegInfty)); assert(_lowerFinite(_rowTypes[r]) == (lhsReal(r) > -realParam(SoPlexBase::INFTY))); if(_upperFinite(_rowTypes[r])) { _rationalLP->changeRhs(r, _feasRhs[r]); _realLP->changeRhs(r, R(_feasRhs[r])); } else if(_realLP->rhs(r) < realParam(SoPlexBase::INFTY)) _realLP->changeRhs(r, realParam(SoPlexBase::INFTY)); assert(_upperFinite(_rowTypes[r]) == (rhsRational(r) < _rationalPosInfty)); assert(_upperFinite(_rowTypes[r]) == (rhsReal(r) < realParam(SoPlexBase::INFTY))); assert(lhsReal(r) <= rhsReal(r)); } // unshift primal space and restore objective coefficients Rational shiftValue; for(int c = numOrigCols - 1; c >= 0; c--) { bool shifted = (_lowerFinite(_colTypes[c]) && _feasLower[c] > 0) || (_upperFinite(_colTypes[c]) && _feasUpper[c] < 0); assert(shifted || !_lowerFinite(_colTypes[c]) || _feasLower[c] == lowerRational(c)); assert(shifted || !_upperFinite(_colTypes[c]) || _feasUpper[c] == upperRational(c)); assert(upperRational(c) >= _rationalPosInfty || lowerRational(c) <= _rationalNegInfty || _feasLower[c] - lowerRational(c) == _feasUpper[c] - upperRational(c)); if(shifted) { if(_lowerFinite(_colTypes[c])) { shiftValue = _feasLower[c]; shiftValue -= lowerRational(c); } else if(_upperFinite(_colTypes[c])) { shiftValue = _feasUpper[c]; shiftValue -= upperRational(c); } if(sol._isPrimalFeasible) { sol._primal[c] += shiftValue; sol._slacks.multAdd(shiftValue, _rationalLP->colVector(c)); } } if(_lowerFinite(_colTypes[c])) { if(shifted) _rationalLP->changeLower(c, _feasLower[c]); _realLP->changeLower(c, R(_feasLower[c])); } else if(_realLP->lower(c) > -realParam(SoPlexBase::INFTY)) _realLP->changeLower(c, -realParam(SoPlexBase::INFTY)); assert(_lowerFinite(_colTypes[c]) == (lowerRational(c) > -_rationalPosInfty)); assert(_lowerFinite(_colTypes[c]) == (lowerReal(c) > -realParam(SoPlexBase::INFTY))); if(_upperFinite(_colTypes[c])) { if(shifted) _rationalLP->changeUpper(c, _feasUpper[c]); _realLP->changeUpper(c, R(upperRational(c))); } else if(_realLP->upper(c) < realParam(SoPlexBase::INFTY)) _realLP->changeUpper(c, realParam(SoPlexBase::INFTY)); assert(_upperFinite(_colTypes[c]) == (upperRational(c) < _rationalPosInfty)); assert(_upperFinite(_colTypes[c]) == (upperReal(c) < realParam(SoPlexBase::INFTY))); _rationalLP->changeMaxObj(c, _feasObj[c]); _realLP->changeMaxObj(c, R(_feasObj[c])); assert(lowerReal(c) <= upperReal(c)); } // remove last column _rationalLP->removeCol(numOrigCols); _realLP->removeCol(numOrigCols); _colTypes.reSize(numOrigCols); // invalidate rational basis factorization _rationalLUSolver.clear(); // print LP if in debug mode MSG_DEBUG(_realLP->writeFileLPBase("afterUntransFeas.lp", 0, 0, 0)); // stop timing _statistics->transformTime->stop(); #ifndef NDEBUG if(sol._isPrimalFeasible) { VectorRational activity(numRowsRational()); _rationalLP->computePrimalActivity(sol._primal, activity); assert(sol._slacks == activity); } #endif } /** computes radius of infeasibility box implied by an approximate Farkas' proof Given constraints of the form \f$ lhs <= Ax <= rhs \f$, a farkas proof y should satisfy \f$ y^T A = 0 \f$ and \f$ y_+^T lhs - y_-^T rhs > 0 \f$, where \f$ y_+, y_- \f$ denote the positive and negative parts of \f$ y \f$. If \f$ y \f$ is approximate, it may not satisfy \f$ y^T A = 0 \f$ exactly, but the proof is still valid as long as the following holds for all potentially feasible \f$ x \f$: \f[ y^T Ax < (y_+^T lhs - y_-^T rhs) (*) \f] we may therefore calculate \f$ y^T A \f$ and \f$ y_+^T lhs - y_-^T rhs \f$ exactly and check if the upper and lower bounds on \f$ x \f$ imply that all feasible \f$ x \f$ satisfy (*), and if not then compute bounds on \f$ x \f$ to guarantee (*). The simplest way to do this is to compute \f[ B = (y_+^T lhs - y_-^T rhs) / \sum_i(|(y^T A)_i|) \f] noting that if every component of \f$ x \f$ has \f$ |x_i| < B \f$, then (*) holds. \f$ B \f$ can be increased by iteratively including variable bounds smaller than \f$ B \f$. The speed of this method can be further improved by using interval arithmetic for all computations. For related information see Sec. 4 of Neumaier and Shcherbina, Mathematical Programming A, 2004. Set transformed to true if this method is called after _transformFeasibility(). */ template void SoPlexBase::_computeInfeasBox(SolRational& sol, bool transformed) { assert(sol.hasDualFarkas()); const VectorRational& lower = transformed ? _feasLower : lowerRational(); const VectorRational& upper = transformed ? _feasUpper : upperRational(); const VectorRational& lhs = transformed ? _feasLhs : lhsRational(); const VectorRational& rhs = transformed ? _feasRhs : rhsRational(); const VectorRational& y = sol._dualFarkas; const int numRows = numRowsRational(); const int numCols = transformed ? numColsRational() - 1 : numColsRational(); SSVectorRational ytransA(numColsRational()); Rational ytransb; Rational temp; // prepare ytransA and ytransb; since we want exact arithmetic, we set the zero threshold of the semi-sparse // vector to zero ytransA.setEpsilon(0); ytransA.clear(); ytransb = 0; ///@todo this currently works only if all constraints are equations aggregate rows and sides using the multipliers of the Farkas ray for(int r = 0; r < numRows; r++) { ytransA += y[r] * _rationalLP->rowVector(r); ytransb += y[r] * (y[r] > 0 ? lhs[r] : rhs[r]); } // if we work on the feasibility problem, we ignore the last column if(transformed) ytransA.reDim(numCols); MSG_DEBUG(std::cout << "ytransb = " << ytransb.str() << "\n"); // if we choose minus ytransb as vector of multipliers for the bound constraints on the variables, we obtain an // exactly feasible dual solution for the LP with zero objective function; we aggregate the bounds of the // variables accordingly and store its negation in temp temp = 0; bool isTempFinite = true; for(int c = 0; c < numCols && isTempFinite; c++) { const Rational& minusRedCost = ytransA[c]; if(minusRedCost > 0) { assert((upper[c] < _rationalPosInfty) == _upperFinite(_colTypes[c])); if(_upperFinite(_colTypes[c])) temp += minusRedCost * upper[c]; else isTempFinite = false; } else if(minusRedCost < 0) { assert((lower[c] > _rationalNegInfty) == _lowerFinite(_colTypes[c])); if(_lowerFinite(_colTypes[c])) temp += minusRedCost * lower[c]; else isTempFinite = false; } } MSG_DEBUG(std::cout << "max ytransA*[x_l,x_u] = " << (isTempFinite ? temp.str() : "infinite") << "\n"); // ytransb - temp is the increase in the dual objective along the Farkas ray; if this is positive, the dual is // unbounded and certifies primal infeasibility if(isTempFinite && temp < ytransb) { MSG_INFO1(spxout, spxout << "Farkas infeasibility proof verified exactly. (1)\n"); return; } // ensure that array of nonzero elements in ytransA is available assert(ytransA.isSetup()); ytransA.setup(); // if ytransb is negative, try to make it zero by including a positive lower bound or a negative upper bound if(ytransb < 0) { for(int c = 0; c < numCols; c++) { if(lower[c] > 0) { ytransA.setValue(c, ytransA[c] - ytransb / lower[c]); ytransb = 0; break; } else if(upper[c] < 0) { ytransA.setValue(c, ytransA[c] - ytransb / upper[c]); ytransb = 0; break; } } } // if ytransb is still zero then the zero solution is inside the bounds and cannot be cut off by the Farkas // constraint; in this case, we cannot compute a Farkas box if(ytransb < 0) { MSG_INFO1(spxout, spxout << "Approximate Farkas proof to weak. Could not compute Farkas box. (1)\n"); return; } // compute the one norm of ytransA temp = 0; const int size = ytransA.size(); for(int n = 0; n < size; n++) temp += spxAbs(ytransA.value(n)); // if the one norm is zero then ytransA is zero the Farkas proof should have been verified above assert(temp != 0); // initialize variables in loop: size of Farkas box B, flag whether B has been increased, and number of current // nonzero in ytransA Rational B = ytransb / temp; bool success = false; int n = 0; // loop through nonzeros of ytransA MSG_DEBUG(std::cout << "B = " << B.str() << "\n"); assert(ytransb >= 0); while(true) { // if all nonzeros have been inspected once without increasing B, we abort; otherwise, we start another round if(n >= ytransA.size()) { if(!success) break; success = false; n = 0; } // get Farkas multiplier of the bound constraint as minus the value in ytransA const Rational& minusRedCost = ytransA.value(n); int colIdx = ytransA.index(n); // if the multiplier is positive we inspect the lower bound: if it is finite and within the Farkas box, we can // increase B by including it in the Farkas proof assert((upper[colIdx] < _rationalPosInfty) == _upperFinite(_colTypes[colIdx])); assert((lower[colIdx] > _rationalNegInfty) == _lowerFinite(_colTypes[colIdx])); if(minusRedCost < 0 && lower[colIdx] > -B && _lowerFinite(_colTypes[colIdx])) { ytransA.clearNum(n); ytransb -= minusRedCost * lower[colIdx]; temp += minusRedCost; assert(ytransb >= 0); assert(temp >= 0); assert(temp == 0 || ytransb / temp > B); // if ytransA and ytransb are zero, we have 0^T x >= 0 and cannot compute a Farkas box if(temp == 0 && ytransb == 0) { MSG_INFO1(spxout, spxout << "Approximate Farkas proof to weak. Could not compute Farkas box. (2)\n"); return; } // if ytransb is positive and ytransA is zero, we have 0^T x > 0, proving infeasibility else if(temp == 0) { assert(ytransb > 0); MSG_INFO1(spxout, spxout << "Farkas infeasibility proof verified exactly. (2)\n"); return; } else { B = ytransb / temp; MSG_DEBUG(std::cout << "B = " << B.str() << "\n"); } success = true; } // if the multiplier is negative we inspect the upper bound: if it is finite and within the Farkas box, we can // increase B by including it in the Farkas proof else if(minusRedCost > 0 && upper[colIdx] < B && _upperFinite(_colTypes[colIdx])) { ytransA.clearNum(n); ytransb -= minusRedCost * upper[colIdx]; temp -= minusRedCost; assert(ytransb >= 0); assert(temp >= 0); assert(temp == 0 || ytransb / temp > B); // if ytransA and ytransb are zero, we have 0^T x >= 0 and cannot compute a Farkas box if(temp == 0 && ytransb == 0) { MSG_INFO1(spxout, spxout << "Approximate Farkas proof to weak. Could not compute Farkas box. (2)\n"); return; } // if ytransb is positive and ytransA is zero, we have 0^T x > 0, proving infeasibility else if(temp == 0) { assert(ytransb > 0); MSG_INFO1(spxout, spxout << "Farkas infeasibility proof verified exactly. (2)\n"); return; } else { B = ytransb / temp; MSG_DEBUG(std::cout << "B = " << B.str() << "\n"); } success = true; } // the multiplier is zero, we can ignore the bound constraints on this variable else if(minusRedCost == 0) ytransA.clearNum(n); // currently this bound cannot be used to increase B; we will check it again in the next round, because B might // have increased by then else n++; } if(B > 0) { MSG_INFO1(spxout, spxout << "Computed Farkas box: provably no feasible solutions with components less than " << B.str() << " in absolute value.\n"); } } // General specializations /// solves real LP during iterative refinement template typename SPxSolverBase::Status SoPlexBase::_solveRealForRational(bool fromscratch, VectorBase& primal, VectorBase& dual, DataArray< typename SPxSolverBase::VarStatus >& basisStatusRows, DataArray< typename SPxSolverBase::VarStatus >& basisStatusCols) { assert(_isConsistent()); assert(_solver.nRows() == numRowsRational()); assert(_solver.nCols() == numColsRational()); assert(primal.dim() == numColsRational()); assert(dual.dim() == numRowsRational()); typename SPxSolverBase::Status result = SPxSolverBase::UNKNOWN; #ifndef SOPLEX_MANUAL_ALT if(fromscratch || !_hasBasis) _enableSimplifierAndScaler(); else _disableSimplifierAndScaler(); #else _disableSimplifierAndScaler(); #endif // reset basis to slack basis when solving from scratch if(fromscratch) _solver.reLoad(); // start timing _statistics->syncTime->start(); // if preprocessing is applied, we need to restore the original LP at the end SPxLPRational* rationalLP = 0; if(_simplifier != 0 || _scaler != nullptr) { spx_alloc(rationalLP); rationalLP = new(rationalLP) SPxLPRational(_solver); } // with preprocessing or solving from scratch, the basis may change, hence invalidate the // rational basis factorization if(_simplifier != nullptr || _scaler != nullptr || fromscratch) _rationalLUSolver.clear(); // stop timing _statistics->syncTime->stop(); try { // apply problem simplification typename SPxSimplifier::Result simplificationStatus = SPxSimplifier::OKAY; if(_simplifier != 0) { // do not remove bounds of boxed variables or sides of ranged rows if bound flipping is used bool keepbounds = intParam(SoPlexBase::RATIOTESTER) == SoPlexBase::RATIOTESTER_BOUNDFLIPPING; Real remainingTime = _solver.getMaxTime() - _solver.time(); simplificationStatus = _simplifier->simplify(_solver, realParam(SoPlexBase::EPSILON_ZERO), realParam(SoPlexBase::FPFEASTOL), realParam(SoPlexBase::FPOPTTOL), remainingTime, keepbounds, _solver.random.getSeed()); } // apply scaling after the simplification if(_scaler != nullptr && simplificationStatus == SPxSimplifier::OKAY) _scaler->scale(_solver, false); // run the simplex method if problem has not been solved by the simplifier if(simplificationStatus == SPxSimplifier::OKAY) { MSG_INFO1(spxout, spxout << std::endl); _solveRealLPAndRecordStatistics(); MSG_INFO1(spxout, spxout << std::endl); } ///@todo move to private helper methods // evaluate status flag if(simplificationStatus == SPxSimplifier::INFEASIBLE) result = SPxSolverBase::INFEASIBLE; else if(simplificationStatus == SPxSimplifier::DUAL_INFEASIBLE) result = SPxSolverBase::INForUNBD; else if(simplificationStatus == SPxSimplifier::UNBOUNDED) result = SPxSolverBase::UNBOUNDED; else if(simplificationStatus == SPxSimplifier::VANISHED || simplificationStatus == SPxSimplifier::OKAY) { result = simplificationStatus == SPxSimplifier::VANISHED ? SPxSolverBase::OPTIMAL : _solver.status(); ///@todo move to private helper methods // process result switch(result) { case SPxSolverBase::OPTIMAL: // unsimplify if simplifier is active and LP is solved to optimality; this must be done here and not at solution // query, because we want to have the basis for the original problem if(_simplifier != 0) { assert(!_simplifier->isUnsimplified()); assert(simplificationStatus == SPxSimplifier::VANISHED || simplificationStatus == SPxSimplifier::OKAY); bool vanished = simplificationStatus == SPxSimplifier::VANISHED; // get solution vectors for transformed problem VectorBase tmpPrimal(vanished ? 0 : _solver.nCols()); VectorBase tmpSlacks(vanished ? 0 : _solver.nRows()); VectorBase tmpDual(vanished ? 0 : _solver.nRows()); VectorBase tmpRedCost(vanished ? 0 : _solver.nCols()); if(!vanished) { assert(_solver.status() == SPxSolverBase::OPTIMAL); _solver.getPrimalSol(tmpPrimal); _solver.getSlacks(tmpSlacks); _solver.getDualSol(tmpDual); _solver.getRedCostSol(tmpRedCost); // unscale vectors if(_scaler != nullptr) { _scaler->unscalePrimal(_solver, tmpPrimal); _scaler->unscaleSlacks(_solver, tmpSlacks); _scaler->unscaleDual(_solver, tmpDual); _scaler->unscaleRedCost(_solver, tmpRedCost); } // get basis of transformed problem basisStatusRows.reSize(_solver.nRows()); basisStatusCols.reSize(_solver.nCols()); _solver.getBasis(basisStatusRows.get_ptr(), basisStatusCols.get_ptr(), basisStatusRows.size(), basisStatusCols.size()); } ///@todo catch exception _simplifier->unsimplify(tmpPrimal, tmpDual, tmpSlacks, tmpRedCost, basisStatusRows.get_ptr(), basisStatusCols.get_ptr()); // store basis for original problem basisStatusRows.reSize(numRowsRational()); basisStatusCols.reSize(numColsRational()); _simplifier->getBasis(basisStatusRows.get_ptr(), basisStatusCols.get_ptr(), basisStatusRows.size(), basisStatusCols.size()); _hasBasis = true; primal = _simplifier->unsimplifiedPrimal(); dual = _simplifier->unsimplifiedDual(); } else { _solver.getPrimalSol(primal); _solver.getDualSol(dual); // unscale vectors if(_scaler != nullptr) { _scaler->unscalePrimal(_solver, primal); _scaler->unscaleDual(_solver, dual); } // get basis of transformed problem basisStatusRows.reSize(_solver.nRows()); basisStatusCols.reSize(_solver.nCols()); _solver.getBasis(basisStatusRows.get_ptr(), basisStatusCols.get_ptr(), basisStatusRows.size(), basisStatusCols.size()); _hasBasis = true; } break; case SPxSolverBase::ABORT_CYCLING: if(_simplifier == 0 && boolParam(SoPlexBase::ACCEPTCYCLING)) { _solver.getPrimalSol(primal); _solver.getDualSol(dual); // unscale vectors if(_scaler != nullptr) { _scaler->unscalePrimal(_solver, primal); _scaler->unscaleDual(_solver, dual); } } // intentional fallthrough case SPxSolverBase::ABORT_TIME: case SPxSolverBase::ABORT_ITER: case SPxSolverBase::ABORT_VALUE: case SPxSolverBase::REGULAR: case SPxSolverBase::RUNNING: case SPxSolverBase::UNBOUNDED: _hasBasis = (_solver.basis().status() > SPxBasisBase::NO_PROBLEM); if(_hasBasis && _simplifier == 0) { basisStatusRows.reSize(_solver.nRows()); basisStatusCols.reSize(_solver.nCols()); _solver.getBasis(basisStatusRows.get_ptr(), basisStatusCols.get_ptr(), basisStatusRows.size(), basisStatusCols.size()); } else { _hasBasis = false; _rationalLUSolver.clear(); } break; case SPxSolverBase::INFEASIBLE: // if simplifier is active we can currently not return a Farkas ray or basis if(_simplifier != 0) { _hasBasis = false; _rationalLUSolver.clear(); break; } // return Farkas ray as dual solution _solver.getDualfarkas(dual); // unscale vectors if(_scaler != nullptr) _scaler->unscaleDual(_solver, dual); // get basis of transformed problem basisStatusRows.reSize(_solver.nRows()); basisStatusCols.reSize(_solver.nCols()); _solver.getBasis(basisStatusRows.get_ptr(), basisStatusCols.get_ptr(), basisStatusRows.size(), basisStatusCols.size()); _hasBasis = true; break; case SPxSolverBase::INForUNBD: case SPxSolverBase::SINGULAR: default: _hasBasis = false; _rationalLUSolver.clear(); break; } } } catch(...) { MSG_INFO1(spxout, spxout << "Exception thrown during floating-point solve.\n"); result = SPxSolverBase::ERROR; _hasBasis = false; _rationalLUSolver.clear(); } // restore original LP if necessary if(_simplifier != 0 || _scaler != nullptr) { assert(rationalLP != 0); _solver.loadLP((SPxLPBase)(*rationalLP)); rationalLP->~SPxLPRational(); spx_free(rationalLP); if(_hasBasis) _solver.setBasis(basisStatusRows.get_ptr(), basisStatusCols.get_ptr()); } return result; } /// solves real LP with recovery mechanism template typename SPxSolverBase::Status SoPlexBase::_solveRealStable(bool acceptUnbounded, bool acceptInfeasible, VectorBase& primal, VectorBase& dual, DataArray< typename SPxSolverBase::VarStatus >& basisStatusRows, DataArray< typename SPxSolverBase::VarStatus >& basisStatusCols, const bool forceNoSimplifier) { typename SPxSolverBase::Status result = SPxSolverBase::UNKNOWN; bool fromScratch = false; bool solved = false; bool solvedFromScratch = false; bool initialSolve = true; bool increasedMarkowitz = false; bool relaxedTolerances = false; bool tightenedTolerances = false; bool switchedScaler = false; bool switchedSimplifier = false; bool switchedRatiotester = false; bool switchedPricer = false; bool turnedoffPre = false; R markowitz = _slufactor.markowitz(); int ratiotester = intParam(SoPlexBase::RATIOTESTER); int pricer = intParam(SoPlexBase::PRICER); int simplifier = intParam(SoPlexBase::SIMPLIFIER); int scaler = intParam(SoPlexBase::SCALER); int type = intParam(SoPlexBase::ALGORITHM); if(forceNoSimplifier) setIntParam(SoPlexBase::SIMPLIFIER, SoPlexBase::SIMPLIFIER_OFF); while(true) { assert(!increasedMarkowitz || GE(_slufactor.markowitz(), R(0.9))); result = _solveRealForRational(fromScratch, primal, dual, basisStatusRows, basisStatusCols); solved = (result == SPxSolverBase::OPTIMAL) || (result == SPxSolverBase::INFEASIBLE && acceptInfeasible) || (result == SPxSolverBase::UNBOUNDED && acceptUnbounded); if(solved || result == SPxSolverBase::ABORT_TIME || result == SPxSolverBase::ABORT_ITER) break; if(initialSolve) { MSG_INFO1(spxout, spxout << "Numerical troubles during floating-point solve." << std::endl); initialSolve = false; } if(!turnedoffPre && (intParam(SoPlexBase::SIMPLIFIER) != SoPlexBase::SIMPLIFIER_OFF || intParam(SoPlexBase::SCALER) != SoPlexBase::SCALER_OFF)) { MSG_INFO1(spxout, spxout << "Turning off preprocessing." << std::endl); turnedoffPre = true; setIntParam(SoPlexBase::SCALER, SoPlexBase::SCALER_OFF); setIntParam(SoPlexBase::SIMPLIFIER, SoPlexBase::SIMPLIFIER_OFF); fromScratch = true; solvedFromScratch = true; continue; } setIntParam(SoPlexBase::SCALER, scaler); setIntParam(SoPlexBase::SIMPLIFIER, simplifier); if(!increasedMarkowitz) { MSG_INFO1(spxout, spxout << "Increasing Markowitz threshold." << std::endl); _slufactor.setMarkowitz(0.9); increasedMarkowitz = true; try { _solver.factorize(); continue; } catch(...) { MSG_DEBUG(std::cout << std::endl << "Factorization failed." << std::endl); } } if(!solvedFromScratch) { MSG_INFO1(spxout, spxout << "Solving from scratch." << std::endl); fromScratch = true; solvedFromScratch = true; continue; } setIntParam(SoPlexBase::RATIOTESTER, ratiotester); setIntParam(SoPlexBase::PRICER, pricer); if(!switchedScaler) { MSG_INFO1(spxout, spxout << "Switching scaling." << std::endl); if(scaler == int(SoPlexBase::SCALER_OFF)) setIntParam(SoPlexBase::SCALER, SoPlexBase::SCALER_BIEQUI); else setIntParam(SoPlexBase::SCALER, SoPlexBase::SCALER_OFF); fromScratch = true; solvedFromScratch = true; switchedScaler = true; continue; } if(!switchedSimplifier && !forceNoSimplifier) { MSG_INFO1(spxout, spxout << "Switching simplification." << std::endl); if(simplifier == int(SoPlexBase::SIMPLIFIER_OFF)) setIntParam(SoPlexBase::SIMPLIFIER, SoPlexBase::SIMPLIFIER_INTERNAL); else setIntParam(SoPlexBase::SIMPLIFIER, SoPlexBase::SIMPLIFIER_OFF); fromScratch = true; solvedFromScratch = true; switchedSimplifier = true; continue; } setIntParam(SoPlexBase::SIMPLIFIER, SoPlexBase::SIMPLIFIER_OFF); if(!relaxedTolerances) { MSG_INFO1(spxout, spxout << "Relaxing tolerances." << std::endl); setIntParam(SoPlexBase::ALGORITHM, SoPlexBase::ALGORITHM_PRIMAL); _solver.setDelta((_solver.feastol() * 1e3 > 1e-3) ? 1e-3 : (_solver.feastol() * 1e3)); relaxedTolerances = _solver.feastol() >= 1e-3; solvedFromScratch = false; continue; } if(!tightenedTolerances && result != SPxSolverBase::INFEASIBLE) { MSG_INFO1(spxout, spxout << "Tightening tolerances." << std::endl); setIntParam(SoPlexBase::ALGORITHM, SoPlexBase::ALGORITHM_DUAL); _solver.setDelta(_solver.feastol() * 1e-3 < 1e-9 ? 1e-9 : _solver.feastol() * 1e-3); tightenedTolerances = _solver.feastol() <= 1e-9; solvedFromScratch = false; continue; } setIntParam(SoPlexBase::ALGORITHM, type); if(!switchedRatiotester) { MSG_INFO1(spxout, spxout << "Switching ratio test." << std::endl); _solver.setType(_solver.type() == SPxSolverBase::LEAVE ? SPxSolverBase::ENTER : SPxSolverBase::LEAVE); if(_solver.ratiotester() != (SPxRatioTester*)&_ratiotesterTextbook) setIntParam(SoPlexBase::RATIOTESTER, RATIOTESTER_TEXTBOOK); else setIntParam(SoPlexBase::RATIOTESTER, RATIOTESTER_FAST); switchedRatiotester = true; solvedFromScratch = false; continue; } if(!switchedPricer) { MSG_INFO1(spxout, spxout << "Switching pricer." << std::endl); _solver.setType(_solver.type() == SPxSolverBase::LEAVE ? SPxSolverBase::ENTER : SPxSolverBase::LEAVE); if(_solver.pricer() != (SPxPricer*)&_pricerDevex) setIntParam(SoPlexBase::PRICER, PRICER_DEVEX); else setIntParam(SoPlexBase::PRICER, PRICER_STEEP); switchedPricer = true; solvedFromScratch = false; continue; } MSG_INFO1(spxout, spxout << "Giving up." << std::endl); break; } _solver.setFeastol(realParam(SoPlexBase::FPFEASTOL)); _solver.setOpttol(realParam(SoPlexBase::FPOPTTOL)); _slufactor.setMarkowitz(markowitz); setIntParam(SoPlexBase::RATIOTESTER, ratiotester); setIntParam(SoPlexBase::PRICER, pricer); setIntParam(SoPlexBase::SIMPLIFIER, simplifier); setIntParam(SoPlexBase::SCALER, scaler); setIntParam(SoPlexBase::ALGORITHM, type); return result; } /// computes rational inverse of basis matrix as defined by _rationalLUSolverBind template void SoPlexBase::_computeBasisInverseRational() { assert(_rationalLUSolver.status() == SLinSolverRational::UNLOADED || _rationalLUSolver.status() == SLinSolverRational::TIME); const int matrixdim = numRowsRational(); assert(_rationalLUSolverBind.size() == matrixdim); Array< const SVectorRational* > matrix(matrixdim); _rationalLUSolverBind.reSize(matrixdim); for(int i = 0; i < matrixdim; i++) { if(_rationalLUSolverBind[i] >= 0) { assert(_rationalLUSolverBind[i] < numColsRational()); matrix[i] = &colVectorRational(_rationalLUSolverBind[i]); } else { assert(-1 - _rationalLUSolverBind[i] >= 0); assert(-1 - _rationalLUSolverBind[i] < numRowsRational()); matrix[i] = _unitVectorRational(-1 - _rationalLUSolverBind[i]); } } // load and factorize rational basis matrix if(realParam(SoPlexBase::TIMELIMIT) < realParam(SoPlexBase::INFTY)) _rationalLUSolver.setTimeLimit((double)realParam(SoPlexBase::TIMELIMIT) - _statistics->solvingTime->time()); else _rationalLUSolver.setTimeLimit(-1.0); _rationalLUSolver.load(matrix.get_ptr(), matrixdim); // record statistics _statistics->luFactorizationTimeRational += _rationalLUSolver.getFactorTime(); _statistics->luFactorizationsRational += _rationalLUSolver.getFactorCount(); _rationalLUSolver.resetCounters(); if(_rationalLUSolver.status() == SLinSolverRational::TIME) { MSG_INFO2(spxout, spxout << "Rational factorization hit time limit.\n"); } else if(_rationalLUSolver.status() != SLinSolverRational::OK) { MSG_INFO1(spxout, spxout << "Error performing rational LU factorization.\n"); } return; } /// factorizes rational basis matrix in column representation template void SoPlexBase::_factorizeColumnRational(SolRational& sol, DataArray< typename SPxSolverBase::VarStatus >& basisStatusRows, DataArray< typename SPxSolverBase::VarStatus >& basisStatusCols, bool& stoppedTime, bool& stoppedIter, bool& error, bool& optimal) { // start rational solving time _statistics->rationalTime->start(); stoppedTime = false; stoppedIter = false; error = false; optimal = false; const bool maximizing = (intParam(SoPlexBase::OBJSENSE) == SoPlexBase::OBJSENSE_MAXIMIZE); const int matrixdim = numRowsRational(); bool loadMatrix = (_rationalLUSolver.status() == SLinSolverRational::UNLOADED || _rationalLUSolver.status() == SLinSolverRational::TIME); int numBasicRows; assert(loadMatrix || matrixdim == _rationalLUSolver.dim()); assert(loadMatrix || matrixdim == _rationalLUSolverBind.size()); if(!loadMatrix && (matrixdim != _rationalLUSolver.dim() || matrixdim != _rationalLUSolverBind.size())) { MSG_WARNING(spxout, spxout << "Warning: dimensioning error in rational matrix factorization (recovered).\n"); loadMatrix = true; } _workSol._primal.reDim(matrixdim); _workSol._slacks.reDim(matrixdim); _workSol._dual.reDim(matrixdim); _workSol._redCost.reDim(numColsRational() > matrixdim ? numColsRational() : matrixdim); if(loadMatrix) _rationalLUSolverBind.reSize(matrixdim); VectorRational& basicPrimalRhs = _workSol._slacks; VectorRational& basicDualRhs = _workSol._redCost; VectorRational& basicPrimal = _workSol._primal; VectorRational& basicDual = _workSol._dual; Rational violation; Rational primalViolation; Rational dualViolation; bool primalFeasible = false; bool dualFeasible = false; assert(basisStatusCols.size() == numColsRational()); assert(basisStatusRows.size() == numRowsRational()); int j = 0; for(int i = 0; i < basisStatusRows.size(); i++) { if(basisStatusRows[i] == SPxSolverBase::BASIC && j < matrixdim) { basicPrimalRhs[i] = 0; basicDualRhs[j] = 0; if(loadMatrix) _rationalLUSolverBind[j] = -1 - i; j++; } else if(basisStatusRows[i] == SPxSolverBase::ON_LOWER) basicPrimalRhs[i] = lhsRational(i); else if(basisStatusRows[i] == SPxSolverBase::ON_UPPER) basicPrimalRhs[i] = rhsRational(i); else if(basisStatusRows[i] == SPxSolverBase::ZERO) basicPrimalRhs[i] = 0; else if(basisStatusRows[i] == SPxSolverBase::FIXED) { assert(lhsRational(i) == rhsRational(i)); basicPrimalRhs[i] = lhsRational(i); } else if(basisStatusRows[i] == SPxSolverBase::UNDEFINED) { MSG_INFO1(spxout, spxout << "Undefined basis status of row in rational factorization.\n"); error = true; goto TERMINATE; } else { assert(basisStatusRows[i] == SPxSolverBase::BASIC); MSG_INFO1(spxout, spxout << "Too many basic rows in rational factorization.\n"); error = true; goto TERMINATE; } } numBasicRows = j; for(int i = 0; i < basisStatusCols.size(); i++) { if(basisStatusCols[i] == SPxSolverBase::BASIC && j < matrixdim) { basicDualRhs[j] = objRational(i); if(loadMatrix) _rationalLUSolverBind[j] = i; j++; } else if(basisStatusCols[i] == SPxSolverBase::ON_LOWER) basicPrimalRhs.multAdd(-lowerRational(i), colVectorRational(i)); else if(basisStatusCols[i] == SPxSolverBase::ON_UPPER) basicPrimalRhs.multAdd(-upperRational(i), colVectorRational(i)); else if(basisStatusCols[i] == SPxSolverBase::ZERO) {} else if(basisStatusCols[i] == SPxSolverBase::FIXED) { assert(lowerRational(i) == upperRational(i)); basicPrimalRhs.multAdd(-lowerRational(i), colVectorRational(i)); } else if(basisStatusCols[i] == SPxSolverBase::UNDEFINED) { MSG_INFO1(spxout, spxout << "Undefined basis status of column in rational factorization.\n"); error = true; goto TERMINATE; } else { assert(basisStatusCols[i] == SPxSolverBase::BASIC); MSG_INFO1(spxout, spxout << "Too many basic columns in rational factorization.\n"); error = true; goto TERMINATE; } } if(j != matrixdim) { MSG_INFO1(spxout, spxout << "Too few basic entries in rational factorization.\n"); error = true; goto TERMINATE; } // load and factorize rational basis matrix if(loadMatrix) _computeBasisInverseRational(); if(_rationalLUSolver.status() == SLinSolverRational::TIME) { stoppedTime = true; return; } else if(_rationalLUSolver.status() != SLinSolverRational::OK) { error = true; return; } assert(_rationalLUSolver.status() == SLinSolverRational::OK); // solve for primal solution if(realParam(SoPlexBase::TIMELIMIT) < realParam(SoPlexBase::INFTY)) _rationalLUSolver.setTimeLimit(Real(realParam(SoPlexBase::TIMELIMIT)) - _statistics->solvingTime->time()); else _rationalLUSolver.setTimeLimit(-1.0); _rationalLUSolver.solveRight(basicPrimal, basicPrimalRhs); // record statistics _statistics->luSolveTimeRational += _rationalLUSolver.getSolveTime(); _rationalLUSolver.resetCounters(); if(_isSolveStopped(stoppedTime, stoppedIter)) { MSG_INFO2(spxout, spxout << "Rational factorization hit time limit while solving for primal.\n"); return; } // check bound violation on basic rows and columns j = 0; primalViolation = 0; primalFeasible = true; for(int i = 0; i < basisStatusRows.size(); i++) { if(basisStatusRows[i] == SPxSolverBase::BASIC) { assert(j < matrixdim); assert(_rationalLUSolverBind[j] == -1 - i); violation = lhsRational(i); violation += basicPrimal[j]; if(violation > primalViolation) { primalFeasible = false; primalViolation = violation; } violation = rhsRational(i); violation += basicPrimal[j]; violation *= -1; if(violation > primalViolation) { primalFeasible = false; primalViolation = violation; } j++; } } for(int i = 0; i < basisStatusCols.size(); i++) { if(basisStatusCols[i] == SPxSolverBase::BASIC) { assert(j < matrixdim); assert(_rationalLUSolverBind[j] == i); if(basicPrimal[j] < lowerRational(i)) { violation = lowerRational(i); violation -= basicPrimal[j]; if(violation > primalViolation) { primalFeasible = false; primalViolation = violation; } } if(basicPrimal[j] > upperRational(i)) { violation = basicPrimal[j]; violation -= upperRational(i); if(violation > primalViolation) { primalFeasible = false; primalViolation = violation; } } j++; } } if(!primalFeasible) { MSG_INFO1(spxout, spxout << "Rational solution primal infeasible.\n"); } // solve for dual solution if(realParam(SoPlexBase::TIMELIMIT) < realParam(SoPlexBase::INFTY)) _rationalLUSolver.setTimeLimit(Real(realParam(SoPlexBase::TIMELIMIT)) - _statistics->solvingTime->time()); else _rationalLUSolver.setTimeLimit(-1.0); _rationalLUSolver.solveLeft(basicDual, basicDualRhs); // record statistics _statistics->luSolveTimeRational += _rationalLUSolver.getSolveTime(); _rationalLUSolver.resetCounters(); if(_isSolveStopped(stoppedTime, stoppedIter)) { MSG_INFO2(spxout, spxout << "Rational factorization hit time limit while solving for dual.\n"); return; } // check dual violation on nonbasic rows dualViolation = 0; dualFeasible = true; for(int i = 0; i < basisStatusRows.size(); i++) { if(_rowTypes[i] == RANGETYPE_FIXED && (basisStatusRows[i] == SPxSolverBase::ON_LOWER || basisStatusRows[i] == SPxSolverBase::ON_UPPER)) { assert(lhsRational(i) == rhsRational(i)); basisStatusRows[i] = SPxSolverBase::FIXED; } assert(basisStatusRows[i] != SPxSolverBase::BASIC || basicDual[i] == 0); if(basisStatusRows[i] == SPxSolverBase::BASIC || basisStatusRows[i] == SPxSolverBase::FIXED) continue; else if(basicDual[i] < 0) { if(((maximizing && basisStatusRows[i] != SPxSolverBase::ON_LOWER) || (!maximizing && basisStatusRows[i] != SPxSolverBase::ON_UPPER)) && (basisStatusRows[i] != SPxSolverBase::ZERO || rhsRational(i) != 0)) { dualFeasible = false; violation = -basicDual[i]; if(violation > dualViolation) dualViolation = violation; MSG_DEBUG(spxout << "negative dual multliplier for row " << i << " with dual " << basicDual[i].str() << " and status " << basisStatusRows[i] << " and [lhs,rhs] = [" << lhsRational(i).str() << "," << rhsRational(i).str() << "]" << "\n"); } } else if(basicDual[i] > 0) { if(((maximizing && basisStatusRows[i] != SPxSolverBase::ON_UPPER) || (!maximizing && basisStatusRows[i] != SPxSolverBase::ON_LOWER)) && (basisStatusRows[i] != SPxSolverBase::ZERO || lhsRational(i) == 0)) { dualFeasible = false; if(basicDual[i] > dualViolation) dualViolation = basicDual[i]; MSG_DEBUG(spxout << "positive dual multliplier for row " << i << " with dual " << basicDual[i].str() << " and status " << basisStatusRows[i] << " and [lhs,rhs] = [" << lhsRational(i).str() << "," << rhsRational(i).str() << "]" << "\n"); } } } // check reduced cost violation on nonbasic columns for(int i = 0; i < basisStatusCols.size(); i++) { if(_colTypes[i] == RANGETYPE_FIXED && (basisStatusCols[i] == SPxSolverBase::ON_LOWER || basisStatusCols[i] == SPxSolverBase::ON_UPPER)) { assert(lowerRational(i) == upperRational(i)); basisStatusCols[i] = SPxSolverBase::FIXED; } assert(basisStatusCols[i] != SPxSolverBase::BASIC || basicDual * colVectorRational(i) == objRational(i)); if(basisStatusCols[i] == SPxSolverBase::BASIC || basisStatusCols[i] == SPxSolverBase::FIXED) continue; else { _workSol._redCost[i] = basicDual * colVectorRational(i); _workSol._redCost[i] -= objRational(i); if(_workSol._redCost[i] > 0) { if(((maximizing && basisStatusCols[i] != SPxSolverBase::ON_LOWER) || (!maximizing && basisStatusCols[i] != SPxSolverBase::ON_UPPER)) && (basisStatusCols[i] != SPxSolverBase::ZERO || upperRational(i) != 0)) { dualFeasible = false; if(_workSol._redCost[i] > dualViolation) dualViolation = _workSol._redCost[i]; } _workSol._redCost[i] *= -1; } else if(_workSol._redCost[i] < 0) { _workSol._redCost[i] *= -1; if(((maximizing && basisStatusCols[i] != SPxSolverBase::ON_UPPER) || (!maximizing && basisStatusCols[i] != SPxSolverBase::ON_LOWER)) && (basisStatusCols[i] != SPxSolverBase::ZERO || lowerRational(i) != 0)) { dualFeasible = false; if(_workSol._redCost[i] > dualViolation) dualViolation = _workSol._redCost[i]; } } else _workSol._redCost[i] *= -1; } } if(!dualFeasible) { MSG_INFO1(spxout, spxout << "Rational solution dual infeasible.\n"); } // store solution optimal = primalFeasible && dualFeasible; if(optimal || boolParam(SoPlexBase::RATFACJUMP)) { _hasBasis = true; if(&basisStatusRows != &_basisStatusRows) _basisStatusRows = basisStatusRows; if(&basisStatusCols != &_basisStatusCols) _basisStatusCols = basisStatusCols; sol._primal.reDim(numColsRational()); j = numBasicRows; for(int i = 0; i < basisStatusCols.size(); i++) { if(basisStatusCols[i] == SPxSolverBase::BASIC) { assert(j < matrixdim); assert(_rationalLUSolverBind[j] == i); sol._primal[i] = basicPrimal[j]; j++; } else if(basisStatusCols[i] == SPxSolverBase::ON_LOWER) sol._primal[i] = lowerRational(i); else if(basisStatusCols[i] == SPxSolverBase::ON_UPPER) sol._primal[i] = upperRational(i); else if(basisStatusCols[i] == SPxSolverBase::ZERO) sol._primal[i] = 0; else if(basisStatusCols[i] == SPxSolverBase::FIXED) { assert(lowerRational(i) == upperRational(i)); sol._primal[i] = lowerRational(i); } else { assert(basisStatusCols[i] == SPxSolverBase::UNDEFINED); MSG_INFO1(spxout, spxout << "Undefined basis status of column in rational factorization.\n"); error = true; goto TERMINATE; } } sol._slacks.reDim(numRowsRational()); _rationalLP->computePrimalActivity(sol._primal, sol._slacks); sol._isPrimalFeasible = true; sol._dual = basicDual; for(int i = 0; i < numColsRational(); i++) { if(basisStatusCols[i] == SPxSolverBase::BASIC) sol._redCost[i] = 0; else if(basisStatusCols[i] == SPxSolverBase::FIXED) { sol._redCost[i] = basicDual * colVectorRational(i); sol._redCost[i] -= objRational(i); sol._redCost[i] *= -1; } else sol._redCost[i] = _workSol._redCost[i]; } sol._isDualFeasible = true; } else { _rationalLUSolver.clear(); } TERMINATE: // stop rational solving time _statistics->rationalTime->stop(); return; } /// attempts rational reconstruction of primal-dual solution template bool SoPlexBase::_reconstructSolutionRational(SolRational& sol, DataArray< typename SPxSolverBase::VarStatus >& basisStatusRows, DataArray< typename SPxSolverBase::VarStatus >& basisStatusCols, const Rational& denomBoundSquared) { bool success; bool isSolBasic; DIdxSet basicIndices(numColsRational()); success = false; isSolBasic = true; if(!sol.isPrimalFeasible() || !sol.isDualFeasible()) return success; // start timing and increment statistics counter _statistics->reconstructionTime->start(); _statistics->rationalReconstructions++; // reconstruct primal vector _workSol._primal = sol._primal; for(int j = 0; j < numColsRational(); ++j) { if(basisStatusCols[j] == SPxSolverBase::BASIC) basicIndices.addIdx(j); } success = reconstructVector(_workSol._primal, denomBoundSquared, &basicIndices); if(!success) { MSG_INFO1(spxout, spxout << "Rational reconstruction of primal solution failed.\n"); _statistics->reconstructionTime->stop(); return success; } MSG_DEBUG(spxout << "Rational reconstruction of primal solution successful.\n"); // check violation of bounds for(int c = numColsRational() - 1; c >= 0; c--) { // we want to notify the user whether the reconstructed solution is basic; otherwise, this would be redundant typename SPxSolverBase::VarStatus& basisStatusCol = _basisStatusCols[c]; if((basisStatusCol == SPxSolverBase::FIXED && _workSol._primal[c] != lowerRational(c)) || (basisStatusCol == SPxSolverBase::ON_LOWER && _workSol._primal[c] != lowerRational(c)) || (basisStatusCol == SPxSolverBase::ON_UPPER && _workSol._primal[c] != upperRational(c)) || (basisStatusCol == SPxSolverBase::ZERO && _workSol._primal[c] != 0) || (basisStatusCol == SPxSolverBase::UNDEFINED)) { isSolBasic = false; } if(_lowerFinite(_colTypes[c]) && _workSol._primal[c] < lowerRational(c)) { MSG_DEBUG(std::cout << "Lower bound of variable " << c << " violated by " << (lowerRational(c) - _workSol._primal[c]).str() << "\n"); MSG_INFO1(spxout, spxout << "Reconstructed solution primal infeasible (1).\n"); _statistics->reconstructionTime->stop(); return false; } if(_upperFinite(_colTypes[c]) && _workSol._primal[c] > upperRational(c)) { MSG_DEBUG(std::cout << "Upper bound of variable " << c << " violated by " << (_workSol._primal[c] - upperRational(c)).str() << "\n"); MSG_INFO1(spxout, spxout << "Reconstructed solution primal infeasible (2).\n"); _statistics->reconstructionTime->stop(); return false; } } // compute slacks ///@todo we should compute them one by one so we can abort when encountering an infeasibility _workSol._slacks.reDim(numRowsRational()); _rationalLP->computePrimalActivity(_workSol._primal, _workSol._slacks); // check violation of sides for(int r = numRowsRational() - 1; r >= 0; r--) { // we want to notify the user whether the reconstructed solution is basic; otherwise, this would be redundant typename SPxSolverBase::VarStatus& basisStatusRow = _basisStatusRows[r]; if((basisStatusRow == SPxSolverBase::FIXED && _workSol._slacks[r] != lhsRational(r)) || (basisStatusRow == SPxSolverBase::ON_LOWER && _workSol._slacks[r] != lhsRational(r)) || (basisStatusRow == SPxSolverBase::ON_UPPER && _workSol._slacks[r] != rhsRational(r)) || (basisStatusRow == SPxSolverBase::ZERO && _workSol._slacks[r] != 0) || (basisStatusRow == SPxSolverBase::UNDEFINED)) { isSolBasic = false; } if(_lowerFinite(_rowTypes[r]) && _workSol._slacks[r] < lhsRational(r)) { MSG_DEBUG(std::cout << "Lhs of row " << r << " violated by " << (lhsRational(r) - _workSol._slacks[r]).str() << "\n"); MSG_INFO1(spxout, spxout << "Reconstructed solution primal infeasible (3).\n"); _statistics->reconstructionTime->stop(); return false; } if(_upperFinite(_rowTypes[r]) && _workSol._slacks[r] > rhsRational(r)) { MSG_DEBUG(std::cout << "Rhs of row " << r << " violated by " << (_workSol._slacks[r] - rhsRational(r)) << "\n"); MSG_INFO1(spxout, spxout << "Reconstructed solution primal infeasible (4).\n"); _statistics->reconstructionTime->stop(); return false; } } // reconstruct dual vector _workSol._dual = sol._dual; success = reconstructVector(_workSol._dual, denomBoundSquared); if(!success) { MSG_INFO1(spxout, spxout << "Rational reconstruction of dual solution failed.\n"); _statistics->reconstructionTime->stop(); return success; } MSG_DEBUG(spxout << "Rational reconstruction of dual vector successful.\n"); // check dual multipliers before reduced costs because this check is faster since it does not require the // computation of reduced costs const bool maximizing = (intParam(SoPlexBase::OBJSENSE) == SoPlexBase::OBJSENSE_MAXIMIZE); for(int r = numRowsRational() - 1; r >= 0; r--) { int sig = sign(_workSol._dual[r]); if((!maximizing && sig > 0) || (maximizing && sig < 0)) { if(!_lowerFinite(_rowTypes[r]) || _workSol._slacks[r] > lhsRational(r)) { MSG_DEBUG(std::cout << "complementary slackness violated by row " << r << " with dual " << _workSol._dual[r].str() << " and slack " << _workSol._slacks[r].str() << " not at lhs " << lhsRational(r).str() << "\n"); MSG_INFO1(spxout, spxout << "Reconstructed solution dual infeasible (1).\n"); _statistics->reconstructionTime->stop(); return false; } if(_basisStatusRows[r] != SPxSolverBase::ON_LOWER && _basisStatusRows[r] != SPxSolverBase::FIXED) { if(_basisStatusRows[r] == SPxSolverBase::BASIC || _basisStatusRows[r] == SPxSolverBase::UNDEFINED) isSolBasic = false; else _basisStatusRows[r] = SPxSolverBase::ON_LOWER; } } else if((!maximizing && sig < 0) || (maximizing && sig > 0)) { if(!_upperFinite(_rowTypes[r]) || _workSol._slacks[r] < rhsRational(r)) { MSG_DEBUG(std::cout << "complementary slackness violated by row " << r << " with dual " << _workSol._dual[r].str() << " and slack " << _workSol._slacks[r].str() << " not at rhs " << rhsRational(r).str() << "\n"); MSG_INFO1(spxout, spxout << "Reconstructed solution dual infeasible (2).\n"); _statistics->reconstructionTime->stop(); return false; } if(_basisStatusRows[r] != SPxSolverBase::ON_UPPER && _basisStatusRows[r] != SPxSolverBase::FIXED) { if(_basisStatusRows[r] == SPxSolverBase::BASIC || _basisStatusRows[r] == SPxSolverBase::UNDEFINED) isSolBasic = false; else _basisStatusRows[r] = SPxSolverBase::ON_UPPER; } } } // compute reduced cost vector; we assume that the objective function vector has less nonzeros than the reduced // cost vector, and so multiplying with -1 first and subtracting the dual activity should be faster than adding // the dual activity and negating afterwards ///@todo we should compute them one by one so we can abort when encountering an infeasibility _workSol._redCost.reDim(numColsRational()); _rationalLP->getObj(_workSol._redCost); _rationalLP->subDualActivity(_workSol._dual, _workSol._redCost); // check reduced cost violation for(int c = numColsRational() - 1; c >= 0; c--) { int sig = sign(_workSol._redCost[c]); if((!maximizing && sig > 0) || (maximizing && sig < 0)) { if(!_lowerFinite(_colTypes[c]) || _workSol._primal[c] > lowerRational(c)) { MSG_DEBUG(std::cout << "complementary slackness violated by column " << c << " with reduced cost " << _workSol._redCost[c].str() << " and value " << _workSol._primal[c].str() << " not at lower bound " << lowerRational(c).str() << "\n"); MSG_INFO1(spxout, spxout << "Reconstructed solution dual infeasible (3).\n"); _statistics->reconstructionTime->stop(); return false; } if(_basisStatusCols[c] != SPxSolverBase::ON_LOWER && _basisStatusCols[c] != SPxSolverBase::FIXED) { if(_basisStatusCols[c] == SPxSolverBase::BASIC || _basisStatusCols[c] == SPxSolverBase::UNDEFINED) isSolBasic = false; else _basisStatusCols[c] = SPxSolverBase::ON_LOWER; } } else if((!maximizing && sig < 0) || (maximizing && sig > 0)) { if(!_upperFinite(_colTypes[c]) || _workSol._primal[c] < upperRational(c)) { MSG_DEBUG(std::cout << "complementary slackness violated by column " << c << " with reduced cost " << _workSol._redCost[c].str() << " and value " << _workSol._primal[c].str() << " not at upper bound " << upperRational(c).str() << "\n"); MSG_INFO1(spxout, spxout << "Reconstructed solution dual infeasible (4).\n"); _statistics->reconstructionTime->stop(); return false; } if(_basisStatusCols[c] != SPxSolverBase::ON_UPPER && _basisStatusCols[c] != SPxSolverBase::FIXED) { if(_basisStatusCols[c] == SPxSolverBase::BASIC || _basisStatusCols[c] == SPxSolverBase::UNDEFINED) isSolBasic = false; else _basisStatusCols[c] = SPxSolverBase::ON_UPPER; } } } // update solution sol._primal = _workSol._primal; sol._slacks = _workSol._slacks; sol._dual = _workSol._dual; sol._redCost = _workSol._redCost; if(!isSolBasic) { MSG_WARNING(spxout, spxout << "Warning: Reconstructed solution not basic.\n"); _hasBasis = false; } // stop timing _statistics->reconstructionTime->stop(); return success; } } // namespace soplex