diff --git a/ortools/linear_solver/linear_solver.h b/ortools/linear_solver/linear_solver.h index edcc49ee61e..a0300ff5c44 100644 --- a/ortools/linear_solver/linear_solver.h +++ b/ortools/linear_solver/linear_solver.h @@ -727,12 +727,6 @@ class MPSolver { const std::vector& variable_statuses, const std::vector& constraint_statuses); - void SetStartingLpBasisInt(const std::vector& variable_statuses, - const std::vector& constraint_statuses); - - void GetFinalLpBasisInt(std::vector& variable_statuses, - std::vector& constraint_statuses); - /** * Infinity. * @@ -1801,24 +1795,6 @@ class MPSolverInterface { LOG(FATAL) << "Not supported by this solver."; } - // See MPSolver::SetStartingLpBasis(). - // Do not convert to MPSolver::BasisStatus, use integers - // Sometimes, converting to MPSolver::BasisStatus leads to a loss of information - // For that reason, we give the possibiity to recover the "raw" basis - virtual void SetStartingLpBasisInt( - const std::vector& /* variable_statuses */, - const std::vector& /* constraint_statuses */) { - LOG(FATAL) << "Not supported by this solver."; - } - - // See MPSolver::SetStartingLpBasis(). - // Do not convert to MPSolver::BasisStatus, use integers - virtual void GetFinalLpBasisInt( - std::vector& /* variable_statuses */, - std::vector& /* constraint_statuses */) { - LOG(FATAL) << "Not supported by this solver."; - } - virtual double infinity() { return std::numeric_limits::infinity(); } virtual bool InterruptSolve() { return false; } diff --git a/ortools/linear_solver/xpress_interface.cc b/ortools/linear_solver/xpress_interface.cc index dd73c03a33a..b707c15a7e8 100644 --- a/ortools/linear_solver/xpress_interface.cc +++ b/ortools/linear_solver/xpress_interface.cc @@ -357,12 +357,9 @@ class XpressInterface : public MPSolverInterface { bool IsLP() const override { return !mMip; } bool IsMIP() const override { return mMip; } - void SetStartingLpBasisInt( - const std::vector& variable_statuses, - const std::vector& constraint_statuses) override; - - void GetFinalLpBasisInt(std::vector& variable_statuses, - std::vector& constraint_statuses) override; + void SetStartingLpBasis( + const std::vector& variable_statuses, + const std::vector& constraint_statuses) override; void ExtractNewVariables() override; void ExtractNewConstraints() override; @@ -421,9 +418,6 @@ class XpressInterface : public MPSolverInterface { // SetHint() void AddSolutionHintToOptimizer(); - // Transform XPRESS basis status to MPSolver basis status. - static MPSolver::BasisStatus xformBasisStatus(int xpress_basis_status); - bool readParameters(std::istream& is, char sep); private: @@ -461,11 +455,13 @@ class XpressInterface : public MPSolverInterface { // Hence, we query the status only once and cache the array. This is // much faster in case the basis status of more than one row/column // is required. + + // TODO std::vector mutable mCstat; std::vector mutable mRstat; - std::vector mutable initCstat; - std::vector mutable initRstat; + std::vector mutable initial_variables_basis_status_; + std::vector mutable initial_constraint_basis_status_; // Set up the right-hand side of a constraint from its lower and upper bound. static void MakeRhs(double lb, double ub, double& rhs, char& sense, @@ -481,11 +477,12 @@ class XpressInterface : public MPSolverInterface { MPCallback* callback_ = nullptr; }; -// Transform XPRESS basis status to MPSolver basis status. -static MPSolver::BasisStatus xformBasisStatus(int xpress_basis_status); // Transform MPSolver basis status to XPRESS status -static int convertToXpressBasisStatus( +static int MPSolverToXpressBasisStatus( MPSolver::BasisStatus mpsolver_basis_status); +// Transform XPRESS basis status to MPSolver basis status. +static MPSolver::BasisStatus XpressToMPSolverBasisStatus( + int xpress_basis_status); static std::map& getMapStringControls() { static std::map mapControls = { @@ -1243,7 +1240,7 @@ int64_t XpressInterface::nodes() const { } // Transform a XPRESS basis status to an MPSolver basis status. -MPSolver::BasisStatus XpressInterface::xformBasisStatus( +static MPSolver::BasisStatus XpressToMPSolverBasisStatus( int xpress_basis_status) { switch (xpress_basis_status) { case XPRS_AT_LOWER: @@ -1260,7 +1257,7 @@ MPSolver::BasisStatus XpressInterface::xformBasisStatus( } } -int convertToXpressBasisStatus(MPSolver::BasisStatus mpsolver_basis_status) { +static int MPSolverToXpressBasisStatus(MPSolver::BasisStatus mpsolver_basis_status) { switch (mpsolver_basis_status) { case MPSolver::AT_LOWER_BOUND: return XPRS_AT_LOWER; @@ -1294,7 +1291,7 @@ MPSolver::BasisStatus XpressInterface::row_status(int constraint_index) const { } if (!mRstat.empty()) { - return xformBasisStatus(mRstat[constraint_index]); + return XpressToMPSolverBasisStatus(mRstat[constraint_index]); } else { LOG(FATAL) << "Row basis status not available"; return MPSolver::FREE; @@ -1319,7 +1316,7 @@ MPSolver::BasisStatus XpressInterface::column_status(int variable_index) const { } if (!mCstat.empty()) { - return xformBasisStatus(mCstat[variable_index]); + return XpressToMPSolverBasisStatus(mCstat[variable_index]); } else { LOG(FATAL) << "Column basis status not available"; return MPSolver::FREE; @@ -1745,38 +1742,24 @@ void XpressInterface::SetLpAlgorithm(int value) { CHECK_STATUS(XPRSsetintcontrol(mLp, XPRS_DEFAULTALG, alg)); } } - -// Convert statuses for later use (Solve) -void XpressInterface::SetStartingLpBasisInt( - const std::vector& variable_statuses, - const std::vector& constraint_statuses) { - if (mMip) { - LOG(DFATAL) << __FUNCTION__ << " is only available for LP problems"; - return; - } - // Column = variable - initCstat = variable_statuses; - // Row = constraint - initRstat = constraint_statuses; +std::vector XpressBasisStatusesFrom( + const std::vector& statuses) { + std::vector result; + result.reserve(statuses.size()); + std::transform(statuses.cbegin(), statuses.cend(), result.begin(), + MPSolverToXpressBasisStatus); + return result; } -void XpressInterface::GetFinalLpBasisInt( - std::vector& variable_statuses, - std::vector& constraint_statuses) { +void XpressInterface::SetStartingLpBasis( + const std::vector& variable_statuses, + const std::vector& constraint_statuses){ if (mMip) { LOG(DFATAL) << __FUNCTION__ << " is only available for LP problems"; return; } - - const int rows = getnumrows(mLp); - const int cols = getnumcols(mLp); - // 1. Resize vectors if needed - variable_statuses.resize(cols); - constraint_statuses.resize(rows); - - // 2. Extract basis - CHECK_STATUS( - XPRSgetbasis(mLp, constraint_statuses.data(), variable_statuses.data())); + initial_variables_basis_status_ = XpressBasisStatusesFrom(variable_statuses); + initial_constraint_basis_status_ = XpressBasisStatusesFrom(constraint_statuses); } bool XpressInterface::readParameters(std::istream& is, char sep) { @@ -1889,8 +1872,9 @@ MPSolver::ResultStatus XpressInterface::Solve(MPSolverParameters const& param) { // Load basis if present // TODO : check number of variables / constraints - if (!mMip && !initCstat.empty() && !initRstat.empty()) { - CHECK_STATUS(XPRSloadbasis(mLp, initRstat.data(), initCstat.data())); + if (!mMip && !initial_variables_basis_status_.empty() && !initial_constraint_basis_status_.empty()) { + CHECK_STATUS(XPRSloadbasis(mLp, initial_constraint_basis_status_.data(), + initial_variables_basis_status_.data())); } // Set the hint (if any) @@ -2085,7 +2069,7 @@ void XpressInterface::Write(const std::string& filename) { VLOG(1) << "Writing Xpress MPS \"" << filename << "\"."; const int status = XPRSwriteprob(mLp, filename.c_str(), ""); if (status) { - LOG(ERROR) << "Xpress: Failed to write MPS! "; + LOG(ERROR) << "Xpress: Failed to write MPS!"; } } diff --git a/ortools/linear_solver/xpress_interface_test.cc b/ortools/linear_solver/xpress_interface_test.cc index 4df44841be8..184fe4e3596 100644 --- a/ortools/linear_solver/xpress_interface_test.cc +++ b/ortools/linear_solver/xpress_interface_test.cc @@ -226,6 +226,25 @@ void buildLargeMip(MPSolver& solver, int numVars, int maxTime) { solver.EnableOutput(); } +void buildLargeLp(MPSolver& solver, int numVars) { + MPObjective* obj = solver.MutableObjective(); + obj->SetMaximization(); + for (int i = 0; i < numVars; ++i) { + MPVariable* x = solver.MakeNumVar(-(i * i) % 21, + (i * i) % 55, + "x_" + std::to_string(i)); + obj->SetCoefficient(x, (i * i) % 23); + int min = -50; + int max = (i * i) % 664 + 55; + MPConstraint* c = solver.MakeRowConstraint(min, max); + c->SetCoefficient(x, i % 331); + for (int j = 0; j < i; ++j) { + c->SetCoefficient(solver.variable(j), i + j); + } + } + solver.EnableOutput(); +} + class MyMPCallback : public MPCallback { private: MPSolver* mpSolver_; @@ -282,6 +301,34 @@ TEST(XpressInterface, isLP) { EXPECT_EQ(solver.IsMIP(), false); } +TEST(XpressInterface, LpStartingBasis) { + UNITTEST_INIT_LP(); + buildLargeLp(solver, 1000); + // First, we record the number of iterations without an initial basis + solver.Solve(); + const auto iterInit = solver.iterations(); + EXPECT_GE(iterInit, 1000); + + // Here, we retrieve the final basis + std::vector varStatus, constrStatus; + for (auto* var : solver.variables()) { + varStatus.push_back(var->basis_status()); + } + for (auto* constr : solver.constraints()) { + constrStatus.push_back(constr->basis_status()); + } + + // Then we slightly modify the problem... + MPObjective* obj = solver.MutableObjective(); + obj->SetCoefficient(solver.variable(1), 100); + // Here, we provide the final basis of the previous (similar) problem + solver.SetStartingLpBasis(varStatus, constrStatus); + solver.Solve(); + const auto iterWithBasis = solver.iterations(); + // ...and check that few iterations have been performed + EXPECT_LT(iterWithBasis, 10); +} + TEST(XpressInterface, NumVariables) { UNITTEST_INIT_MIP(); MPVariable* x1 = solver.MakeNumVar(-1., 5.1, "x1"); @@ -1307,7 +1354,6 @@ TEST(XpressInterface, CallbackThrowsException) { } // namespace operations_research int main(int argc, char** argv) { - InitGoogle(argv[0], &argc, &argv, true); absl::SetFlag(&FLAGS_logtostderr, 1); testing::InitGoogleTest(&argc, argv); auto solver = operations_research::MPSolver::CreateSolver("XPRESS_LP");