Skip to content

Commit

Permalink
Formation of standard form works for minimizations
Browse files Browse the repository at this point in the history
  • Loading branch information
jajhall committed Oct 26, 2024
1 parent 18dffed commit afe2f9a
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 80 deletions.
27 changes: 14 additions & 13 deletions check/TestLpSolvers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -478,23 +478,23 @@ void testStandardForm(const HighsLp& lp) {
highs.run();
// highs.writeSolution("", kSolutionStylePretty);
double required_objective_function_value =
highs.getInfo().objective_function_value;
highs.getInfo().objective_function_value;

HighsInt num_col;
HighsInt num_row;
HighsInt num_nz;
double offset;
REQUIRE(highs.getStandardFormLp(num_col, num_row, num_nz, offset) ==
HighsStatus::kOk);
HighsStatus::kOk);

std::vector<double> cost(num_col);
std::vector<double> rhs(num_row);
std::vector<HighsInt> start(num_col + 1);
std::vector<HighsInt> index(num_nz);
std::vector<double> value(num_nz);
REQUIRE(highs.getStandardFormLp(num_col, num_row, num_nz, offset, cost.data(),
rhs.data(), start.data(), index.data(),
value.data()) == HighsStatus::kOk);
rhs.data(), start.data(), index.data(),
value.data()) == HighsStatus::kOk);

HighsLp standard_form_lp;
standard_form_lp.num_col_ = num_col;
Expand All @@ -515,8 +515,8 @@ void testStandardForm(const HighsLp& lp) {
highs.writeSolution("", kSolutionStylePretty);
double objective_function_value = highs.getInfo().objective_function_value;
double objective_difference =
std::fabs(objective_function_value - required_objective_function_value) /
std::max(1.0, std::fabs(required_objective_function_value));
std::fabs(objective_function_value - required_objective_function_value) /
std::max(1.0, std::fabs(required_objective_function_value));
REQUIRE(objective_difference < 1e-10);
const bool look_at_presolved_lp = false;
if (look_at_presolved_lp) {
Expand All @@ -532,7 +532,9 @@ void testStandardForm(const HighsLp& lp) {
}

void testStandardFormModel(const std::string model) {
const std::string model_file = std::string(HIGHS_DIR) + "/check/instances/" + model + ".mps";;
const std::string model_file =
std::string(HIGHS_DIR) + "/check/instances/" + model + ".mps";
;
Highs highs;
highs.setOptionValue("output_flag", dev_run);
highs.readModel(model_file);
Expand Down Expand Up @@ -564,18 +566,17 @@ TEST_CASE("standard-form-lp", "[highs_lp_solver]") {
highs.setOptionValue("output_flag", dev_run);

std::vector<HighsInt> index;
std::vector<double> value;
std::vector<double> value;
// Add a fixed column and a fixed row
highs.passModel(lp);
index = {0, 1, 2};
value = {-1, 1, -1};
REQUIRE(highs.addCol(-2.0, 1.0, 1.0, 3, index.data(), value.data()) == HighsStatus::kOk);
REQUIRE(highs.addCol(-2.0, 1.0, 1.0, 3, index.data(), value.data()) ==
HighsStatus::kOk);
index = {0, 1, 2, 3};
value = {-2, -1, 1, 3};
REQUIRE(highs.addRow(1.0, 1.0, 4, index.data(), value.data()) == HighsStatus::kOk);
REQUIRE(highs.addRow(1.0, 1.0, 4, index.data(), value.data()) ==
HighsStatus::kOk);

testStandardForm(highs.getLp());



}
119 changes: 56 additions & 63 deletions src/lp_data/HighsInterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,8 @@ HighsStatus Highs::formStandardFormLp() {
// columns are assumed to come before any new columns, so their
// costs must be defined befor costs of new columns.
this->standard_form_cost_ = lp.col_cost_;
// Whilst debugging, work with local data structure
double standard_form_offset;
std::vector<double> standard_form_cost = lp.col_cost_;
std::vector<double> standard_form_rhs;
HighsSparseMatrix standard_form_matrix;
standard_form_matrix.format_ = MatrixFormat::kRowwise;
standard_form_matrix.num_col_ = lp.num_col_;
this->standard_form_matrix_.format_ = MatrixFormat::kRowwise;
this->standard_form_matrix_.num_col_ = lp.num_col_;
// Create a HighsSparseMatrix instance to store rows extracted from
// the original constraint matrix
HighsInt local_row_min_nnz = std::max(lp.num_col_, HighsInt(2));
Expand Down Expand Up @@ -77,45 +72,45 @@ HighsStatus Highs::formStandardFormLp() {
num_fixed_row++;
matrix.getRow(iRow, num_nz, local_row.index_.data(),
local_row.value_.data());
standard_form_matrix.addRows(local_row);
standard_form_rhs.push_back(upper);
this->standard_form_matrix_.addRows(local_row);
this->standard_form_rhs_.push_back(upper);
continue;
} else if (lower <= -kHighsInf) {
// Upper bounded row, so record the slack
num_upper_row++;
assert(upper < kHighsInf);
HighsInt standard_form_row = standard_form_rhs.size();
slack_ix.push_back(standard_form_row+1);
HighsInt standard_form_row = this->standard_form_rhs_.size();
slack_ix.push_back(standard_form_row + 1);
matrix.getRow(iRow, num_nz, local_row.index_.data(),
local_row.value_.data());
standard_form_matrix.addRows(local_row);
standard_form_rhs.push_back(upper);
this->standard_form_matrix_.addRows(local_row);
this->standard_form_rhs_.push_back(upper);
} else if (upper >= kHighsInf) {
// Lower bounded row, so record the slack
num_lower_row++;
assert(lower > -kHighsInf);
HighsInt standard_form_row = standard_form_rhs.size();
slack_ix.push_back(-(standard_form_row+1));
HighsInt standard_form_row = this->standard_form_rhs_.size();
slack_ix.push_back(-(standard_form_row + 1));
matrix.getRow(iRow, num_nz, local_row.index_.data(),
local_row.value_.data());
standard_form_matrix.addRows(local_row);
standard_form_rhs.push_back(lower);
this->standard_form_matrix_.addRows(local_row);
this->standard_form_rhs_.push_back(lower);
} else {
// Boxed row, so record the lower slack
assert(lower > -kHighsInf);
assert(upper < kHighsInf);
num_boxed_row++;
HighsInt standard_form_row = standard_form_rhs.size();
slack_ix.push_back(-(standard_form_row+1));
HighsInt standard_form_row = this->standard_form_rhs_.size();
slack_ix.push_back(-(standard_form_row + 1));
matrix.getRow(iRow, num_nz, local_row.index_.data(),
local_row.value_.data());
standard_form_matrix.addRows(local_row);
standard_form_rhs.push_back(lower);
this->standard_form_matrix_.addRows(local_row);
this->standard_form_rhs_.push_back(lower);
// .. and upper slack, adding a copy of the row
standard_form_row = standard_form_rhs.size();
slack_ix.push_back(standard_form_row+1);
standard_form_matrix.addRows(local_row);
standard_form_rhs.push_back(upper);
standard_form_row = this->standard_form_rhs_.size();
slack_ix.push_back(standard_form_row + 1);
this->standard_form_matrix_.addRows(local_row);
this->standard_form_rhs_.push_back(upper);
}
}
// Add rows corresponding to boxed columns
Expand All @@ -130,27 +125,27 @@ HighsStatus Highs::formStandardFormLp() {
// Introduce variable s >= 0 so that (with x >= l still)
//
// x = u - s => x + s = u
standard_form_cost.push_back(0);
standard_form_matrix.num_col_++;
this->standard_form_cost_.push_back(0);
this->standard_form_matrix_.num_col_++;
local_row.num_col_++;
local_row.index_[0] = iCol;
local_row.index_[1] = standard_form_matrix.num_col_ - 1;
local_row.index_[1] = this->standard_form_matrix_.num_col_ - 1;
local_row.value_[0] = 1;
local_row.value_[1] = 1;
local_row.start_[1] = 2;
standard_form_matrix.addRows(local_row);
standard_form_rhs.push_back(upper);
this->standard_form_matrix_.addRows(local_row);
this->standard_form_rhs_.push_back(upper);
}
}
// Finished with both matrices, row-wise, so ensure that the
// incumbent matrix leaves col-wise, and that the standard form
// matrix is col-wise so RHS shifts can be applied and more columns
// can be added
matrix.ensureColwise();
standard_form_matrix.ensureColwise();
this->standard_form_matrix_.ensureColwise();
// Determine the objective scaling, and apply it to any offset
double objective_mu = double(lp.sense_);
standard_form_offset = objective_mu * lp.offset_;
this->standard_form_offset_ = objective_mu * lp.offset_;
// Work through the columns, ensuring that all have non-negativity bounds
for (HighsInt iCol = 0; iCol < lp.num_col_; iCol++) {
double cost = objective_mu * lp.col_cost_[iCol];
Expand All @@ -174,12 +169,12 @@ HighsStatus Highs::formStandardFormLp() {
// x >= l, so shift x-l = X >= 0, giving x = X + l
//
// Cost contribution c(X+l) = cX + cl
standard_form_offset += cost * lower;
this->standard_form_offset_ += cost * lower;
// Constraint contribution a(X+l) = aX + al
for (HighsInt iEl = standard_form_matrix.start_[iCol];
iEl < standard_form_matrix.start_[iCol + 1]; iEl++)
standard_form_rhs[standard_form_matrix.index_[iEl]] -=
standard_form_matrix.value_[iEl] * lower;
for (HighsInt iEl = this->standard_form_matrix_.start_[iCol];
iEl < this->standard_form_matrix_.start_[iCol + 1]; iEl++)
this->standard_form_rhs_[this->standard_form_matrix_.index_[iEl]] -=
this->standard_form_matrix_.value_[iEl] * lower;
}
} else if (upper < kHighsInf) {
// Upper column
Expand All @@ -189,14 +184,15 @@ HighsStatus Highs::formStandardFormLp() {
// x <= u, so shift u-x = X >= 0, giving x = u - X
//
// Cost contribution c(u-X) = cu - cX
standard_form_offset += cost * upper;
standard_form_cost[iCol] = -cost;
this->standard_form_offset_ += cost * upper;
this->standard_form_cost_[iCol] = -cost;
// Constraint contribution a(u-X) = -aX + au
for (HighsInt iEl = standard_form_matrix.start_[iCol];
iEl < standard_form_matrix.start_[iCol + 1]; iEl++) {
standard_form_rhs[standard_form_matrix.index_[iEl]] -=
standard_form_matrix.value_[iEl] * upper;
standard_form_matrix.value_[iEl] = -standard_form_matrix.value_[iEl];
for (HighsInt iEl = this->standard_form_matrix_.start_[iCol];
iEl < this->standard_form_matrix_.start_[iCol + 1]; iEl++) {
this->standard_form_rhs_[this->standard_form_matrix_.index_[iEl]] -=
this->standard_form_matrix_.value_[iEl] * upper;
this->standard_form_matrix_.value_[iEl] =
-this->standard_form_matrix_.value_[iEl];
}
} else {
// Free column
Expand All @@ -206,36 +202,33 @@ HighsStatus Highs::formStandardFormLp() {
// where original column is now x+ >= 0
//
// and x- >= 0 has negation of its cost and matrix column
standard_form_cost.push_back(-cost);
for (HighsInt iEl = standard_form_matrix.start_[iCol];
iEl < standard_form_matrix.start_[iCol + 1]; iEl++) {
standard_form_matrix.index_.push_back(standard_form_matrix.index_[iEl]);
standard_form_matrix.value_.push_back(
-standard_form_matrix.value_[iEl]);
this->standard_form_cost_.push_back(-cost);
for (HighsInt iEl = this->standard_form_matrix_.start_[iCol];
iEl < this->standard_form_matrix_.start_[iCol + 1]; iEl++) {
this->standard_form_matrix_.index_.push_back(
this->standard_form_matrix_.index_[iEl]);
this->standard_form_matrix_.value_.push_back(
-this->standard_form_matrix_.value_[iEl]);
}
standard_form_matrix.start_.push_back(
HighsInt(standard_form_matrix.index_.size()));
this->standard_form_matrix_.start_.push_back(
HighsInt(this->standard_form_matrix_.index_.size()));
}
}
// Now add the slack variables
for (HighsInt iX = 0; iX < HighsInt(slack_ix.size()); iX++) {
HighsInt iRow = slack_ix[iX];
standard_form_cost.push_back(0);
this->standard_form_cost_.push_back(0);
if (iRow > 0) {
standard_form_matrix.index_.push_back(iRow - 1);
standard_form_matrix.value_.push_back(1);
this->standard_form_matrix_.index_.push_back(iRow - 1);
this->standard_form_matrix_.value_.push_back(1);
} else {
standard_form_matrix.index_.push_back(-iRow - 1);
standard_form_matrix.value_.push_back(-1);
this->standard_form_matrix_.index_.push_back(-iRow - 1);
this->standard_form_matrix_.value_.push_back(-1);
}
standard_form_matrix.start_.push_back(
HighsInt(standard_form_matrix.index_.size()));
this->standard_form_matrix_.start_.push_back(
HighsInt(this->standard_form_matrix_.index_.size()));
}
this->standard_form_valid_ = true;
this->standard_form_offset_ = standard_form_offset;
this->standard_form_cost_ = standard_form_cost;
this->standard_form_rhs_ = standard_form_rhs;
this->standard_form_matrix_ = standard_form_matrix;
highsLogUser(options_.log_options, HighsLogType::kInfo,
"Standard form LP obtained for LP with (free / lower / upper / "
"boxed / fixed) variables"
Expand Down
4 changes: 2 additions & 2 deletions src/qpsolver/dantzigpricing.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,9 @@ class DantzigPricing : public Pricing {

public:
DantzigPricing(Runtime& rt, Basis& bas, ReducedCosts& rc)
//clang-format off
// clang-format off
: runtime(rt), basis(bas), redcosts(rc) {};
//clang-format on
// clang-format on

HighsInt price(const QpVector& x, const QpVector& gradient) {
HighsInt minidx = chooseconstrainttodrop(redcosts.getReducedCosts());
Expand Down
4 changes: 2 additions & 2 deletions src/qpsolver/devexpricing.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,9 @@ class DevexPricing : public Pricing {
: runtime(rt),
basis(bas),
redcosts(rc),
//clang-format off
// clang-format off
weights(std::vector<double>(rt.instance.num_var, 1.0)) {};
//clang-format on
// clang-format on

// B lambda = g
// lambda = inv(B)g
Expand Down

0 comments on commit afe2f9a

Please sign in to comment.