Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for indicator constraints in XPRESS interface #133

Draft
wants to merge 3 commits into
base: mpsolver_support_indicator_cts
Choose a base branch
from
Draft
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
implement indicator constraints support in xpress interface
Signed-off-by: Peter Mitri <[email protected]>
pet-mit committed Aug 13, 2024
commit 8449326663b67ef3f0ea28d229659bd87222a83d
24 changes: 21 additions & 3 deletions ortools/linear_solver/xpress_interface.cc
Original file line number Diff line number Diff line change
@@ -227,7 +227,7 @@ class XpressMPCallbackContext : public MPCallbackContext {
: xprsprob_(xprsprob),
event_(event),
num_nodes_(num_nodes),
variable_values_(0) {};
variable_values_(0){};

// Implementation of the interface.
MPCallbackEvent Event() override { return event_; };
@@ -260,7 +260,7 @@ class XpressMPCallbackContext : public MPCallbackContext {
// Wraps the MPCallback in order to catch and store exceptions
class MPCallbackWrapper {
public:
explicit MPCallbackWrapper(MPCallback* callback) : callback_(callback) {};
explicit MPCallbackWrapper(MPCallback* callback) : callback_(callback){};
MPCallback* GetCallback() const { return callback_; }
// Since our (C++) call-back functions are called from the XPRESS (C) code,
// exceptions thrown in our call-back code are not caught by XPRESS.
@@ -1542,6 +1542,10 @@ void XpressInterface::ExtractNewConstraints() {
unique_ptr<char[]> sense(new char[chunk]);
unique_ptr<double[]> rhs(new double[chunk]);
unique_ptr<double[]> rngval(new double[chunk]);
int n_indicators = 0;
std::vector<int> indicator_rowind;
std::vector<int> indicator_colind;
std::vector<int> indicator_complement;

// Loop over the new constraints, collecting rows for up to
// CHUNK constraints into the arrays so that adding constraints
@@ -1577,13 +1581,26 @@ void XpressInterface::ExtractNewConstraints() {
++nextNz;
}
}

// Detect & store indicator constraints
if (ct->indicator_variable() != nullptr) {
n_indicators++;
indicator_rowind.push_back(nextRow);
indicator_colind.push_back(ct->indicator_variable()->index());
indicator_complement.push_back(ct->indicator_value() ? 1 : -1);
}
}
if (nextRow > 0) {
CHECK_STATUS(XPRSaddrows(mLp, nextRow, nextNz, sense.get(), rhs.get(),
rngval.get(), rmatbeg.get(), rmatind.get(),
rmatval.get()));
}
}

// Set indicator constraints in XPRESS
CHECK_STATUS(XPRSsetindicators(mLp, n_indicators, indicator_rowind.data(),
indicator_colind.data(),
indicator_complement.data()));
} catch (...) {
// Undo all changes in case of error.
int const rows = getnumrows(mLp);
@@ -2290,7 +2307,8 @@ double XpressMPCallbackContext::SuggestSolution(
}

bool XpressInterface::AddIndicatorConstraint(MPConstraint* ct) {

InvalidateModelSynchronization();
return !IsContinuous();
}

} // namespace operations_research
57 changes: 55 additions & 2 deletions ortools/linear_solver/xpress_interface_test.cc
Original file line number Diff line number Diff line change
@@ -285,9 +285,9 @@ class MyMPCallback : public MPCallback {
MyMPCallback(MPSolver* mpSolver, bool should_throw)
: MPCallback(false, false),
mpSolver_(mpSolver),
should_throw_(should_throw) {};
should_throw_(should_throw){};

~MyMPCallback() override {};
~MyMPCallback() override{};

void RunCallback(MPCallbackContext* callback_context) override {
if (should_throw_) {
@@ -1411,6 +1411,59 @@ TEST_F(XpressFixtureMIP, CallbackThrowsException) {
ASSERT_NE(errors.find(expected_error), std::string::npos);
}

TEST_F(XpressFixtureMIP, IndicatorConstraint0) {
solver.EnableOutput();
// Maximize x <= 100
auto x = solver.MakeNumVar(0, 100, "x");
solver.MutableObjective()->SetMaximization();
solver.MutableObjective()->SetCoefficient(x, 1);
// With indicator constraint
// if var = 0, then x <= 10
auto var = solver.MakeBoolVar("indicator_var");
auto ct = solver.MakeIndicatorConstraint(0, 10, "test", var, false);
ct->SetCoefficient(x, 1);

// Leave var free ==> x = 100
solver.Solve();
EXPECT_EQ(var->solution_value(), 1);
EXPECT_EQ(x->solution_value(), 100);

// Force var to 0 ==> x = 10
// WARNING : can't use var->SetUB(0), because then XPRESS would automatically
// change its type to continuous, then fail on indicator variable evaluation.
// We have to add a constraint instead.
ct = solver.MakeRowConstraint(0, 0, "set_indicator_var_to_0");
ct->SetCoefficient(var, 1);
solver.Solve();
EXPECT_EQ(x->solution_value(), 10);
}

TEST_F(XpressFixtureMIP, IndicatorConstraint1) {
// Maximize x <= 100
auto x = solver.MakeNumVar(0, 100, "x");
solver.MutableObjective()->SetMaximization();
solver.MutableObjective()->SetCoefficient(x, 1);
// With indicator constraint
// if var = 1, then x <= 10
auto var = solver.MakeBoolVar("indicator_var");
auto ct = solver.MakeIndicatorConstraint(0, 10, "test", var, true);
ct->SetCoefficient(x, 1);

// Leave var free ==> x = 100
solver.Solve();
EXPECT_EQ(var->solution_value(), 0);
EXPECT_EQ(x->solution_value(), 100);

// Force var to 0 ==> x = 10
// WARNING : can't use var->SetLB(1), because then XPRESS would automatically
// change its type to continuous, then fail on indicator variable evaluation.
// We have to add a constraint instead.
ct = solver.MakeRowConstraint(1, 1, "set_indicator_var_to_1");
ct->SetCoefficient(var, 1);
solver.Solve();
EXPECT_EQ(x->solution_value(), 10);
}

} // namespace operations_research

int main(int argc, char** argv) {