Skip to content

Commit

Permalink
Merge pull request #113 from rte-france/feature/change_SetStartingLpB…
Browse files Browse the repository at this point in the history
…asis

Feature/change SetStartingLpBasis
  • Loading branch information
sgatto authored Nov 17, 2023
2 parents 0a08de7 + 254fd48 commit 17ed6aa
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 72 deletions.
24 changes: 0 additions & 24 deletions ortools/linear_solver/linear_solver.h
Original file line number Diff line number Diff line change
Expand Up @@ -727,12 +727,6 @@ class MPSolver {
const std::vector<MPSolver::BasisStatus>& variable_statuses,
const std::vector<MPSolver::BasisStatus>& constraint_statuses);

void SetStartingLpBasisInt(const std::vector<int>& variable_statuses,
const std::vector<int>& constraint_statuses);

void GetFinalLpBasisInt(std::vector<int>& variable_statuses,
std::vector<int>& constraint_statuses);

/**
* Infinity.
*
Expand Down Expand Up @@ -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<int>& /* variable_statuses */,
const std::vector<int>& /* 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<int>& /* variable_statuses */,
std::vector<int>& /* constraint_statuses */) {
LOG(FATAL) << "Not supported by this solver.";
}

virtual double infinity() { return std::numeric_limits<double>::infinity(); }

virtual bool InterruptSolve() { return false; }
Expand Down
78 changes: 31 additions & 47 deletions ortools/linear_solver/xpress_interface.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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<int>& variable_statuses,
const std::vector<int>& constraint_statuses) override;

void GetFinalLpBasisInt(std::vector<int>& variable_statuses,
std::vector<int>& constraint_statuses) override;
void SetStartingLpBasis(
const std::vector<MPSolver::BasisStatus>& variable_statuses,
const std::vector<MPSolver::BasisStatus>& constraint_statuses) override;

void ExtractNewVariables() override;
void ExtractNewConstraints() override;
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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<int> mutable mCstat;
std::vector<int> mutable mRstat;

std::vector<int> mutable initCstat;
std::vector<int> mutable initRstat;
std::vector<int> mutable initial_variables_basis_status_;
std::vector<int> 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,
Expand All @@ -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<std::string, int>& getMapStringControls() {
static std::map<std::string, int> mapControls = {
Expand Down Expand Up @@ -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:
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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<int>& variable_statuses,
const std::vector<int>& 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<int> XpressBasisStatusesFrom(
const std::vector<MPSolver::BasisStatus>& statuses) {
std::vector<int> result;
result.reserve(statuses.size());
std::transform(statuses.cbegin(), statuses.cend(), result.begin(),
MPSolverToXpressBasisStatus);
return result;
}

void XpressInterface::GetFinalLpBasisInt(
std::vector<int>& variable_statuses,
std::vector<int>& constraint_statuses) {
void XpressInterface::SetStartingLpBasis(
const std::vector<MPSolver::BasisStatus>& variable_statuses,
const std::vector<MPSolver::BasisStatus>& 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) {
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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!";
}
}

Expand Down
48 changes: 47 additions & 1 deletion ortools/linear_solver/xpress_interface_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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_;
Expand Down Expand Up @@ -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<MPSolver::BasisStatus> 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");
Expand Down Expand Up @@ -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");
Expand Down

0 comments on commit 17ed6aa

Please sign in to comment.