diff --git a/check/TestLpSolvers.cpp b/check/TestLpSolvers.cpp index 572a478cf2..6ef4fb4692 100644 --- a/check/TestLpSolvers.cpp +++ b/check/TestLpSolvers.cpp @@ -2,7 +2,7 @@ #include "Highs.h" #include "catch.hpp" -const bool dev_run = false; +const bool dev_run = true; // false; struct IterationCount { HighsInt simplex; @@ -283,7 +283,7 @@ TEST_CASE("LP-solver", "[highs_lp_solver]") { const HighsInfo& info = highs.getInfo(); REQUIRE(info.num_dual_infeasibilities == 0); - REQUIRE(info.simplex_iteration_count == 472); // 476); // 444); + REQUIRE(info.simplex_iteration_count == 472); HighsModelStatus model_status = highs.getModelStatus(); REQUIRE(model_status == HighsModelStatus::kOptimal); @@ -296,7 +296,7 @@ TEST_CASE("LP-solver", "[highs_lp_solver]") { return_status = highs.run(); REQUIRE(return_status == HighsStatus::kOk); - REQUIRE(info.simplex_iteration_count == 592); // 621); // 584); // + REQUIRE(info.simplex_iteration_count == 592); } TEST_CASE("mip-with-lp-solver", "[highs_lp_solver]") { @@ -470,3 +470,117 @@ TEST_CASE("blending-lp-ipm", "[highs_lp_solver]") { printf("Sum dual infeasibilities = %g\n", info.sum_dual_infeasibilities); } } + +void testStandardForm(const HighsLp& lp) { + Highs highs; + highs.setOptionValue("output_flag", dev_run); + HighsInt sense = HighsInt(lp.sense_); + highs.passModel(lp); + highs.run(); + // highs.writeSolution("", kSolutionStylePretty); + double required_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); + + std::vector cost(num_col); + std::vector rhs(num_row); + std::vector start(num_col + 1); + std::vector index(num_nz); + std::vector 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); + + HighsLp standard_form_lp; + standard_form_lp.num_col_ = num_col; + standard_form_lp.num_row_ = num_row; + standard_form_lp.offset_ = offset; + standard_form_lp.col_cost_ = cost; + standard_form_lp.col_lower_.assign(num_col, 0); + standard_form_lp.col_upper_.assign(num_col, kHighsInf); + standard_form_lp.row_lower_ = rhs; + standard_form_lp.row_upper_ = rhs; + standard_form_lp.a_matrix_.start_ = start; + standard_form_lp.a_matrix_.index_ = index; + standard_form_lp.a_matrix_.value_ = value; + REQUIRE(highs.passModel(standard_form_lp) == HighsStatus::kOk); + // highs.writeModel(""); + REQUIRE(highs.run() == HighsStatus::kOk); + REQUIRE(highs.getModelStatus() == HighsModelStatus::kOptimal); + highs.writeSolution("", kSolutionStylePretty); + double objective_function_value = + sense * 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)); + REQUIRE(objective_difference < 1e-10); + const bool look_at_presolved_lp = false; + if (look_at_presolved_lp) { + // Strange that presolve doesn't convert the constraints + // + // Ax-s = b; s >= 0 into Ax >= b + REQUIRE(highs.passModel(standard_form_lp) == HighsStatus::kOk); + REQUIRE(highs.presolve() == HighsStatus::kOk); + HighsLp presolved_lp = highs.getPresolvedLp(); + REQUIRE(highs.passModel(presolved_lp) == HighsStatus::kOk); + highs.writeModel(""); + } +} + +void testStandardFormModel(const std::string model) { + 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); + HighsLp lp = highs.getLp(); + testStandardForm(lp); +} + +TEST_CASE("standard-form-mps", "[highs_lp_solver]") { + testStandardFormModel("avgas"); + testStandardFormModel("afiro"); +} + +TEST_CASE("standard-form-lp", "[highs_lp_solver]") { + HighsLp lp; + lp.offset_ = -0.5; + lp.num_col_ = 4; + lp.num_row_ = 3; + lp.col_cost_ = {1, 1, 1, -1}; + lp.col_lower_ = {1, -kHighsInf, -kHighsInf, -1}; + lp.col_upper_ = {kHighsInf, kHighsInf, 2, 3}; + lp.row_lower_ = {0, 1, -kHighsInf}; + lp.row_upper_ = {4, kHighsInf, 4}; + lp.a_matrix_.start_ = {0, 2, 4, 6, 8}; + lp.a_matrix_.index_ = {0, 2, 0, 1, 1, 2, 0, 2}; + lp.a_matrix_.value_ = {1, 1, 1, 1, 1, 1, 1, 1}; + + testStandardForm(lp); + Highs highs; + highs.setOptionValue("output_flag", dev_run); + + std::vector index; + std::vector value; + // Add a fixed column and a fixed row, and maximize + 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); + 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.changeObjectiveSense(ObjSense::kMaximize) == HighsStatus::kOk); + printf( + "\nNow test by adding a fixed column and a fixed row, and maximizing\n"); + testStandardForm(highs.getLp()); +} diff --git a/src/Highs.h b/src/Highs.h index 22e29c52cc..724a177375 100644 --- a/src/Highs.h +++ b/src/Highs.h @@ -400,6 +400,17 @@ class Highs { * Methods for model output */ + /** + * @brief Identify and the standard form of the HighsLp instance in + * HiGHS + */ + HighsStatus getStandardFormLp(HighsInt& num_col, HighsInt& num_row, + HighsInt& num_nz, double& offset, + double* cost = nullptr, double* rhs = nullptr, + HighsInt* start = nullptr, + HighsInt* index = nullptr, + double* value = nullptr); + /** * @brief Return a const reference to the presolved HighsLp instance in HiGHS */ @@ -1378,6 +1389,12 @@ class Highs { HighsPresolveStatus::kNotPresolved; HighsModelStatus model_status_ = HighsModelStatus::kNotset; + bool standard_form_valid_; + double standard_form_offset_; + std::vector standard_form_cost_; + std::vector standard_form_rhs_; + HighsSparseMatrix standard_form_matrix_; + HEkk ekk_instance_; HighsPresolveLog presolve_log_; @@ -1430,6 +1447,9 @@ class Highs { // Clears the presolved model and its status void clearPresolve(); // + // Clears the standard form LP + void clearStandardFormLp(); + // // Methods to clear solver data for users in Highs class members // before (possibly) updating them with data from trying to solve // the incumbent model. @@ -1473,6 +1493,7 @@ class Highs { void underDevelopmentLogMessage(const std::string& method_name); // Interface methods + HighsStatus formStandardFormLp(); HighsStatus basisForSolution(); HighsStatus addColsInterface( HighsInt ext_num_new_col, const double* ext_col_cost, diff --git a/src/lp_data/Highs.cpp b/src/lp_data/Highs.cpp index c1f1f48b72..43769ef474 100644 --- a/src/lp_data/Highs.cpp +++ b/src/lp_data/Highs.cpp @@ -65,6 +65,7 @@ HighsStatus Highs::clearModel() { HighsStatus Highs::clearSolver() { HighsStatus return_status = HighsStatus::kOk; clearPresolve(); + clearStandardFormLp(); invalidateUserSolverData(); return returnFromHighs(return_status); } @@ -1637,6 +1638,37 @@ HighsStatus Highs::run() { return returnFromRun(return_status, undo_mods); } +HighsStatus Highs::getStandardFormLp(HighsInt& num_col, HighsInt& num_row, + HighsInt& num_nz, double& offset, + double* cost, double* rhs, HighsInt* start, + HighsInt* index, double* value) { + if (!this->standard_form_valid_) { + HighsStatus status = formStandardFormLp(); + assert(status == HighsStatus::kOk); + } + num_col = this->standard_form_cost_.size(); + num_row = this->standard_form_rhs_.size(); + num_nz = this->standard_form_matrix_.start_[num_col]; + offset = this->standard_form_offset_; + for (HighsInt iCol = 0; iCol < num_col; iCol++) { + if (cost) cost[iCol] = this->standard_form_cost_[iCol]; + if (start) start[iCol] = this->standard_form_matrix_.start_[iCol]; + if (index || value) { + for (HighsInt iEl = this->standard_form_matrix_.start_[iCol]; + iEl < this->standard_form_matrix_.start_[iCol + 1]; iEl++) { + if (index) index[iEl] = this->standard_form_matrix_.index_[iEl]; + if (value) value[iEl] = this->standard_form_matrix_.value_[iEl]; + } + } + } + if (start) start[num_col] = this->standard_form_matrix_.start_[num_col]; + if (rhs) { + for (HighsInt iRow = 0; iRow < num_row; iRow++) + rhs[iRow] = this->standard_form_rhs_[iRow]; + } + return HighsStatus::kOk; +} + HighsStatus Highs::getDualRay(bool& has_dual_ray, double* dual_ray_value) { // Can't get a ray without an INVERT, but absence is only an error // when solving an LP #1350 @@ -2338,6 +2370,7 @@ HighsStatus Highs::addCols(const HighsInt num_new_col, const double* costs, this->logHeader(); HighsStatus return_status = HighsStatus::kOk; clearPresolve(); + clearStandardFormLp(); return_status = interpretCallStatus( options_.log_options, addColsInterface(num_new_col, costs, lower_bounds, upper_bounds, @@ -2376,6 +2409,7 @@ HighsStatus Highs::addRows(const HighsInt num_new_row, this->logHeader(); HighsStatus return_status = HighsStatus::kOk; clearPresolve(); + clearStandardFormLp(); return_status = interpretCallStatus( options_.log_options, addRowsInterface(num_new_row, lower_bounds, upper_bounds, num_new_nz, @@ -2391,6 +2425,7 @@ HighsStatus Highs::changeObjectiveSense(const ObjSense sense) { model_.lp_.sense_ = sense; // Nontrivial change clearPresolve(); + clearStandardFormLp(); invalidateModelStatusSolutionAndInfo(); } return returnFromHighs(HighsStatus::kOk); @@ -2520,6 +2555,7 @@ HighsStatus Highs::changeColCost(const HighsInt col, const double cost) { HighsStatus Highs::changeColsCost(const HighsInt from_col, const HighsInt to_col, const double* cost) { clearPresolve(); + clearStandardFormLp(); HighsIndexCollection index_collection; const HighsInt create_error = create(index_collection, from_col, to_col, model_.lp_.num_col_); @@ -2546,6 +2582,7 @@ HighsStatus Highs::changeColsCost(const HighsInt num_set_entries, if (doubleUserDataNotNull(options_.log_options, cost, "column costs")) return HighsStatus::kError; clearPresolve(); + clearStandardFormLp(); // Ensure that the set and data are in ascending order std::vector local_cost{cost, cost + num_set_entries}; std::vector local_set{set, set + num_set_entries}; @@ -2569,6 +2606,7 @@ HighsStatus Highs::changeColsCost(const HighsInt num_set_entries, HighsStatus Highs::changeColsCost(const HighsInt* mask, const double* cost) { clearPresolve(); + clearStandardFormLp(); HighsIndexCollection index_collection; const bool create_error = create(index_collection, mask, model_.lp_.num_col_); assert(!create_error); @@ -2590,6 +2628,7 @@ HighsStatus Highs::changeColsBounds(const HighsInt from_col, const HighsInt to_col, const double* lower, const double* upper) { clearPresolve(); + clearStandardFormLp(); HighsIndexCollection index_collection; const HighsInt create_error = create(index_collection, from_col, to_col, model_.lp_.num_col_); @@ -2624,6 +2663,7 @@ HighsStatus Highs::changeColsBounds(const HighsInt num_set_entries, null_data; if (null_data) return HighsStatus::kError; clearPresolve(); + clearStandardFormLp(); // Ensure that the set and data are in ascending order std::vector local_lower{lower, lower + num_set_entries}; std::vector local_upper{upper, upper + num_set_entries}; @@ -2649,6 +2689,7 @@ HighsStatus Highs::changeColsBounds(const HighsInt num_set_entries, HighsStatus Highs::changeColsBounds(const HighsInt* mask, const double* lower, const double* upper) { clearPresolve(); + clearStandardFormLp(); HighsIndexCollection index_collection; const bool create_error = create(index_collection, mask, model_.lp_.num_col_); assert(!create_error); @@ -2671,6 +2712,7 @@ HighsStatus Highs::changeRowsBounds(const HighsInt from_row, const HighsInt to_row, const double* lower, const double* upper) { clearPresolve(); + clearStandardFormLp(); HighsIndexCollection index_collection; const HighsInt create_error = create(index_collection, from_row, to_row, model_.lp_.num_row_); @@ -2705,6 +2747,7 @@ HighsStatus Highs::changeRowsBounds(const HighsInt num_set_entries, null_data; if (null_data) return HighsStatus::kError; clearPresolve(); + clearStandardFormLp(); // Ensure that the set and data are in ascending order std::vector local_lower{lower, lower + num_set_entries}; std::vector local_upper{upper, upper + num_set_entries}; @@ -2730,6 +2773,7 @@ HighsStatus Highs::changeRowsBounds(const HighsInt num_set_entries, HighsStatus Highs::changeRowsBounds(const HighsInt* mask, const double* lower, const double* upper) { clearPresolve(); + clearStandardFormLp(); HighsIndexCollection index_collection; const bool create_error = create(index_collection, mask, model_.lp_.num_row_); assert(!create_error); @@ -3028,6 +3072,7 @@ HighsStatus Highs::getCoeff(const HighsInt row, const HighsInt col, HighsStatus Highs::deleteCols(const HighsInt from_col, const HighsInt to_col) { clearPresolve(); + clearStandardFormLp(); HighsIndexCollection index_collection; const HighsInt create_error = create(index_collection, from_col, to_col, model_.lp_.num_col_); @@ -3046,6 +3091,7 @@ HighsStatus Highs::deleteCols(const HighsInt num_set_entries, const HighsInt* set) { if (num_set_entries == 0) return HighsStatus::kOk; clearPresolve(); + clearStandardFormLp(); HighsIndexCollection index_collection; const HighsInt create_error = create(index_collection, num_set_entries, set, model_.lp_.num_col_); @@ -3059,6 +3105,7 @@ HighsStatus Highs::deleteCols(const HighsInt num_set_entries, HighsStatus Highs::deleteCols(HighsInt* mask) { clearPresolve(); + clearStandardFormLp(); const HighsInt original_num_col = model_.lp_.num_col_; HighsIndexCollection index_collection; const bool create_error = create(index_collection, mask, original_num_col); @@ -3072,6 +3119,7 @@ HighsStatus Highs::deleteCols(HighsInt* mask) { HighsStatus Highs::deleteRows(const HighsInt from_row, const HighsInt to_row) { clearPresolve(); + clearStandardFormLp(); HighsIndexCollection index_collection; const HighsInt create_error = create(index_collection, from_row, to_row, model_.lp_.num_row_); @@ -3090,6 +3138,7 @@ HighsStatus Highs::deleteRows(const HighsInt num_set_entries, const HighsInt* set) { if (num_set_entries == 0) return HighsStatus::kOk; clearPresolve(); + clearStandardFormLp(); HighsIndexCollection index_collection; const HighsInt create_error = create(index_collection, num_set_entries, set, model_.lp_.num_row_); @@ -3103,6 +3152,7 @@ HighsStatus Highs::deleteRows(const HighsInt num_set_entries, HighsStatus Highs::deleteRows(HighsInt* mask) { clearPresolve(); + clearStandardFormLp(); const HighsInt original_num_row = model_.lp_.num_row_; HighsIndexCollection index_collection; const bool create_error = create(index_collection, mask, original_num_row); @@ -3117,6 +3167,7 @@ HighsStatus Highs::deleteRows(HighsInt* mask) { HighsStatus Highs::scaleCol(const HighsInt col, const double scale_value) { HighsStatus return_status = HighsStatus::kOk; clearPresolve(); + clearStandardFormLp(); HighsStatus call_status = scaleColInterface(col, scale_value); return_status = interpretCallStatus(options_.log_options, call_status, return_status, "scaleCol"); @@ -3127,6 +3178,7 @@ HighsStatus Highs::scaleCol(const HighsInt col, const double scale_value) { HighsStatus Highs::scaleRow(const HighsInt row, const double scale_value) { HighsStatus return_status = HighsStatus::kOk; clearPresolve(); + clearStandardFormLp(); HighsStatus call_status = scaleRowInterface(row, scale_value); return_status = interpretCallStatus(options_.log_options, call_status, return_status, "scaleRow"); @@ -3422,6 +3474,14 @@ void Highs::clearPresolve() { presolve_.clear(); } +void Highs::clearStandardFormLp() { + standard_form_valid_ = false; + standard_form_offset_ = 0; + standard_form_cost_.clear(); + standard_form_rhs_.clear(); + standard_form_matrix_.clear(); +} + void Highs::invalidateUserSolverData() { invalidateModelStatus(); invalidateSolution(); diff --git a/src/lp_data/HighsIis.cpp b/src/lp_data/HighsIis.cpp index 9f244415ca..06dffcabb2 100644 --- a/src/lp_data/HighsIis.cpp +++ b/src/lp_data/HighsIis.cpp @@ -40,7 +40,7 @@ void HighsIis::report(const std::string message, const HighsLp& lp) const { if (num_iis_col > 10 || num_iis_row > 10) return; printf("\nIIS %s\n===\n", message.c_str()); printf("Column: "); - for (HighsInt iCol = 0; iCol < num_iis_col; iCol++) printf("%9d ", iCol); + for (HighsInt iCol = 0; iCol < num_iis_col; iCol++) printf("%9d ", int(iCol)); printf("\nStatus: "); for (HighsInt iCol = 0; iCol < num_iis_col; iCol++) printf("%9s ", iisBoundStatusToString(this->col_bound_[iCol]).c_str()); diff --git a/src/lp_data/HighsInterface.cpp b/src/lp_data/HighsInterface.cpp index 1366b7c927..a669339dc4 100644 --- a/src/lp_data/HighsInterface.cpp +++ b/src/lp_data/HighsInterface.cpp @@ -21,6 +21,228 @@ #include "util/HighsMatrixUtils.h" #include "util/HighsSort.h" +HighsStatus Highs::formStandardFormLp() { + this->clearStandardFormLp(); + HighsLp& lp = this->model_.lp_; + HighsSparseMatrix& matrix = lp.a_matrix_; + // Ensure that the incumbent LP and standard form LP matrices are rowwise + matrix.ensureRowwise(); + // Original rows are processed before columns, so that any original + // boxed rows can be transformed to pairs of one-sided rows, + // requiring the standard form matrix to be row-wise. The original + // columns are assumed to come before any new columns, so their + // costs (as a minimization) must be defined befor costs of new + // columns. + // Determine the objective scaling, and apply it to any offset + HighsInt sense = HighsInt(lp.sense_); + this->standard_form_offset_ = sense * lp.offset_; + for (HighsInt iCol = 0; iCol < lp.num_col_; iCol++) + this->standard_form_cost_.push_back(sense * lp.col_cost_[iCol]); + 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)); + HighsSparseMatrix local_row; + local_row.ensureRowwise(); + local_row.num_row_ = 1; + local_row.num_col_ = lp.num_col_; + local_row.index_.resize(local_row_min_nnz); + local_row.value_.resize(local_row_min_nnz); + local_row.start_.resize(2); + HighsInt& num_nz = local_row.start_[1]; + local_row.start_[0] = 0; + HighsInt num_fixed_row = 0; + HighsInt num_boxed_row = 0; + HighsInt num_lower_row = 0; + HighsInt num_upper_row = 0; + HighsInt num_free_row = 0; + HighsInt num_fixed_col = 0; + HighsInt num_boxed_col = 0; + HighsInt num_lower_col = 0; + HighsInt num_upper_col = 0; + HighsInt num_free_col = 0; + std::vector slack_ix; + for (HighsInt iRow = 0; iRow < lp.num_row_; iRow++) { + double lower = lp.row_lower_[iRow]; + double upper = lp.row_upper_[iRow]; + if (lower <= -kHighsInf && upper >= kHighsInf) { + assert(0 == 1); + // Free row + num_free_row++; + continue; + } + if (lower == upper) { + // Equality row + num_fixed_row++; + matrix.getRow(iRow, num_nz, local_row.index_.data(), + local_row.value_.data()); + 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 = 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()); + 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 = 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()); + 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 = 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()); + 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 = 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 + for (HighsInt iCol = 0; iCol < lp.num_col_; iCol++) { + double lower = lp.col_lower_[iCol]; + double upper = lp.col_upper_[iCol]; + if (lower > -kHighsInf && upper < kHighsInf) { + // Boxed column + // + // x will be replaced by x = l + X (below) with X >= 0 + // + // Introduce variable s >= 0 so that (with x >= l still) + // + // x = u - s => x + s = u + 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] = this->standard_form_matrix_.num_col_ - 1; + local_row.value_[0] = 1; + local_row.value_[1] = 1; + local_row.start_[1] = 2; + 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(); + this->standard_form_matrix_.ensureColwise(); + // Work through the columns, ensuring that all have non-negativity bounds + for (HighsInt iCol = 0; iCol < lp.num_col_; iCol++) { + double cost = sense * lp.col_cost_[iCol]; + double lower = lp.col_lower_[iCol]; + double upper = lp.col_upper_[iCol]; + if (lower > -kHighsInf) { + // Finite lower bound + if (upper < kHighsInf) { + if (lower == upper) { + // Fixed column + num_fixed_col++; + } else { + // Boxed column + num_boxed_col++; + } + } else { + // Lower column + num_lower_col++; + } + if (lower != 0) { + // x >= l, so shift x-l = X >= 0, giving x = X + l + // + // Cost contribution c(X+l) = cX + cl + this->standard_form_offset_ += cost * lower; + // Constraint contribution a(X+l) = aX + al + 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 + num_upper_col++; + // Have to operate even if u=0, since cost and column values are negated + // + // x <= u, so shift u-x = X >= 0, giving x = u - X + // + // Cost contribution c(u-X) = cu - cX + this->standard_form_offset_ += cost * upper; + this->standard_form_cost_[iCol] = -cost; + // Constraint contribution a(u-X) = -aX + au + 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 + num_free_col++; + // Represent as x = x+ - x- + // + // where original column is now x+ >= 0 + // + // and x- >= 0 has negation of its cost and matrix column + 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]); + } + 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]; + this->standard_form_cost_.push_back(0); + if (iRow > 0) { + this->standard_form_matrix_.index_.push_back(iRow - 1); + this->standard_form_matrix_.value_.push_back(1); + } else { + this->standard_form_matrix_.index_.push_back(-iRow - 1); + this->standard_form_matrix_.value_.push_back(-1); + } + this->standard_form_matrix_.start_.push_back( + HighsInt(this->standard_form_matrix_.index_.size())); + } + this->standard_form_valid_ = true; + highsLogUser(options_.log_options, HighsLogType::kInfo, + "Standard form LP obtained for LP with (free / lower / upper / " + "boxed / fixed) variables" + " (%d / %d / %d / %d / %d) and constraints" + " (%d / %d / %d / %d / %d) \n", + int(num_free_col), int(num_lower_col), int(num_upper_col), + int(num_boxed_col), int(num_fixed_col), int(num_free_row), + int(num_lower_row), int(num_upper_row), int(num_boxed_row), + int(num_fixed_row)); + return HighsStatus::kOk; +} + HighsStatus Highs::basisForSolution() { HighsLp& lp = model_.lp_; assert(!lp.isMip() || options_.solve_relaxation); diff --git a/src/mip/HighsLpRelaxation.cpp b/src/mip/HighsLpRelaxation.cpp index db6edef910..3242248cef 100644 --- a/src/mip/HighsLpRelaxation.cpp +++ b/src/mip/HighsLpRelaxation.cpp @@ -1147,14 +1147,11 @@ HighsLpRelaxation::Status HighsLpRelaxation::run(bool resolve_on_error) { // postsolve cannot be run, so there is no basis. Returning // Status::kError as a result yielded #1962, where the root node // is unbounded. - if (info.basis_validity == kBasisValidityInvalid) { + if (info.basis_validity == kBasisValidityInvalid) highsLogUser(mipsolver.options_mip_->log_options, HighsLogType::kWarning, "HighsLpRelaxation::run LP is unbounded with no basis, " "but not returning Status::kError\n"); - // return Status::kError; - } - if (info.primal_solution_status == kSolutionStatusFeasible) mipsolver.mipdata_->trySolution(lpsolver.getSolution().col_value, 'T'); diff --git a/src/qpsolver/dantzigpricing.hpp b/src/qpsolver/dantzigpricing.hpp index 89b109e5c1..05b0d9c00c 100644 --- a/src/qpsolver/dantzigpricing.hpp +++ b/src/qpsolver/dantzigpricing.hpp @@ -52,7 +52,9 @@ class DantzigPricing : public Pricing { public: DantzigPricing(Runtime& rt, Basis& bas, ReducedCosts& rc) + // clang-format off : runtime(rt), basis(bas), redcosts(rc) {}; + // clang-format on HighsInt price(const QpVector& x, const QpVector& gradient) { HighsInt minidx = chooseconstrainttodrop(redcosts.getReducedCosts()); diff --git a/src/qpsolver/devexpricing.hpp b/src/qpsolver/devexpricing.hpp index a88f3ecea5..6664a98611 100644 --- a/src/qpsolver/devexpricing.hpp +++ b/src/qpsolver/devexpricing.hpp @@ -58,7 +58,9 @@ class DevexPricing : public Pricing { : runtime(rt), basis(bas), redcosts(rc), + // clang-format off weights(std::vector(rt.instance.num_var, 1.0)) {}; + // clang-format on // B lambda = g // lambda = inv(B)g