diff --git a/check/TestLpModification.cpp b/check/TestLpModification.cpp index be24bf6605..9a443ea43f 100644 --- a/check/TestLpModification.cpp +++ b/check/TestLpModification.cpp @@ -2273,7 +2273,6 @@ TEST_CASE("row-wise-get-row-avgas", "[highs_data]") { h.ensureColwise(); testAvgasGetRow(h); testAvgasGetCol(h); - } TEST_CASE("hot-start-after-delete", "[highs_data]") { @@ -2285,54 +2284,194 @@ TEST_CASE("hot-start-after-delete", "[highs_data]") { const HighsSolution& solution = h.getSolution(); std::string model = "avgas"; std::string model_file = - std::string(HIGHS_DIR) + "/check/instances/" + model + ".mps"; + std::string(HIGHS_DIR) + "/check/instances/" + model + ".mps"; h.readModel(model_file); h.run(); - printf("Initial solve takes %d iterations and yields objective = %g\n", - int(info.simplex_iteration_count), info.objective_function_value); - h.writeSolution("", 1); - for (HighsInt iCol = 0; iCol < lp.num_col_; iCol++) - printf("Col %2d is %s\n", int(iCol), basis.col_status[iCol] == HighsBasisStatus::kBasic ? "basic" : "nonbasic"); - printf("\n"); - for (HighsInt iRow = 0; iRow < lp.num_row_; iRow++) - printf("Row %2d is %s\n", int(iRow), basis.row_status[iRow] == HighsBasisStatus::kBasic ? "basic" : "nonbasic"); + HighsInt ieration_count0 = info.simplex_iteration_count; + if (dev_run) + printf("Initial solve takes %d iterations and yields objective = %g\n", + int(info.simplex_iteration_count), info.objective_function_value); - double cost; - double lower; - double upper; + HighsInt max_dim = std::max(lp.num_col_, lp.num_row_); + std::vector cost(1); + std::vector lower(1); + std::vector upper(1); HighsInt nnz; std::vector start(1); - std::vector index(lp.num_row_); - std::vector value(lp.num_row_); + std::vector index(max_dim); + std::vector value(max_dim); HighsInt get_num; + HighsInt use_col, use_row; + for (HighsInt k = 0; k < 2; k++) { + if (dev_run) { + for (HighsInt iCol = 0; iCol < lp.num_col_; iCol++) + printf("Col %2d is %s\n", int(iCol), + basis.col_status[iCol] == HighsBasisStatus::kBasic ? "basic" + : "nonbasic"); + printf("\n"); + } + if (k == 0) { + use_col = 1; // Nonbasic + } else { + use_col = 4; // Basic + } + if (dev_run) + printf( + "\nDeleting and adding column %1d with status \"%s\" and value %g\n", + int(use_col), + h.basisStatusToString(basis.col_status[use_col]).c_str(), + solution.col_value[use_col]); + + h.getCols(use_col, use_col, get_num, cost.data(), lower.data(), + upper.data(), nnz, start.data(), index.data(), value.data()); + + h.deleteCols(use_col, use_col); + if (dev_run) basis.printScalars(); + + h.addCol(cost[0], lower[0], upper[0], nnz, index.data(), value.data()); + + h.run(); + if (dev_run) + printf( + "After deleting and adding column %1d, solve takes %d iterations and " + "yields objective = %g\n", + int(use_col), int(info.simplex_iteration_count), + info.objective_function_value); + REQUIRE(info.simplex_iteration_count < ieration_count0); + } + + for (HighsInt k = 0; k < 2; k++) { + if (dev_run) { + for (HighsInt iRow = 0; iRow < lp.num_row_; iRow++) + printf("Row %2d is %s\n", int(iRow), + basis.row_status[iRow] == HighsBasisStatus::kBasic ? "basic" + : "nonbasic"); + } + if (k == 0) { + use_row = 1; // Nonbasic + } else { + use_row = 8; // Basic + } + if (dev_run) + printf("\nDeleting and adding row %1d with status \"%s\" and value %g\n", + int(use_row), + h.basisStatusToString(basis.row_status[use_row]).c_str(), + solution.row_value[use_row]); + + h.getRows(use_row, use_row, get_num, lower.data(), upper.data(), nnz, + start.data(), index.data(), value.data()); + + h.deleteRows(use_row, use_row); + if (dev_run) basis.printScalars(); + + h.addRow(lower[0], upper[0], nnz, index.data(), value.data()); + + h.run(); + if (dev_run) + printf( + "After deleting and adding row %1d, solve takes %d iterations and " + "yields objective = %g\n", + int(use_row), int(info.simplex_iteration_count), + info.objective_function_value); + REQUIRE(info.simplex_iteration_count < ieration_count0); + } + std::vector set = {1, 3, 4}; + HighsInt num_set_en = set.size(); + cost.resize(num_set_en); + lower.resize(num_set_en); + upper.resize(num_set_en); + start.resize(num_set_en); + index.resize(num_set_en * max_dim); + value.resize(num_set_en * max_dim); + + h.getCols(num_set_en, set.data(), get_num, cost.data(), lower.data(), + upper.data(), nnz, start.data(), index.data(), value.data()); + + h.deleteCols(num_set_en, set.data()); + if (dev_run) basis.printScalars(); + + h.addCols(get_num, cost.data(), lower.data(), upper.data(), nnz, start.data(), + index.data(), value.data()); + + h.run(); + if (dev_run) + printf( + "After deleting and adding %d columns in set, solve takes %d " + "iterations and yields objective = %g\n", + int(get_num), int(info.simplex_iteration_count), + info.objective_function_value); + // REQUIRE(info.simplex_iteration_count < ieration_count0); + + h.getRows(num_set_en, set.data(), get_num, lower.data(), upper.data(), nnz, + start.data(), index.data(), value.data()); + + h.deleteRows(num_set_en, set.data()); + if (dev_run) basis.printScalars(); + + h.addRows(get_num, lower.data(), upper.data(), nnz, start.data(), + index.data(), value.data()); + + h.run(); + if (dev_run) + printf( + "After deleting and adding %d rows in set, solve takes %d iterations " + "and yields objective = %g\n", + int(get_num), int(info.simplex_iteration_count), + info.objective_function_value); + // REQUIRE(info.simplex_iteration_count < ieration_count0); + std::vector mask; + mask.assign(max_dim, 0); + mask[1] = 1; + mask[4] = 1; + mask[5] = 1; + + h.getCols(mask.data(), get_num, cost.data(), lower.data(), upper.data(), nnz, + start.data(), index.data(), value.data()); + + h.deleteCols(mask.data()); + if (dev_run) basis.printScalars(); + + h.addCols(get_num, cost.data(), lower.data(), upper.data(), nnz, start.data(), + index.data(), value.data()); - HighsInt use_col = 1; - printf("\nDeleting and adding column %1d with status \"%s\" and value %g\n", - int(use_col), h.basisStatusToString(basis.col_status[use_col]).c_str(), solution.col_value[use_col]); - - h.getCols(use_col, use_col, get_num, &cost, &lower, &upper, nnz, start.data(), index.data(), value.data()); - - h.deleteCols(use_col, use_col); - basis.printScalars(); - - h.addCol(cost, lower, upper, nnz, index.data(), value.data()); - h.run(); - printf("After deleting and adding column %1d, solve takes %d iterations and yields objective = %g\n", - int(use_col), int(info.simplex_iteration_count), info.objective_function_value); - - HighsInt use_row = 1; - printf("\nDeleting and adding row %1d with status \"%s\" and value %g\n", - int(use_row), h.basisStatusToString(basis.row_status[use_row]).c_str(), solution.row_value[use_row]); - - h.getRows(use_row, use_row, get_num, &lower, &upper, nnz, start.data(), index.data(), value.data()); - - h.deleteRows(use_row, use_row); - basis.printScalars(); - - h.addRow(lower, upper, nnz, index.data(), value.data()); - + if (dev_run) + printf( + "After deleting and adding %d columns in mask, solve takes %d " + "iterations and yields objective = %g\n", + int(get_num), int(info.simplex_iteration_count), + info.objective_function_value); + // REQUIRE(info.simplex_iteration_count < ieration_count0); + + mask.assign(max_dim, 0); + mask[1] = 1; + mask[4] = 1; + mask[5] = 1; + mask[8] = 1; + mask[9] = 1; + HighsInt num_mask_en = mask.size(); + cost.resize(num_mask_en); + lower.resize(num_mask_en); + upper.resize(num_mask_en); + start.resize(num_mask_en); + index.resize(num_mask_en * max_dim); + value.resize(num_mask_en * max_dim); + + h.getRows(mask.data(), get_num, lower.data(), upper.data(), nnz, start.data(), + index.data(), value.data()); + + h.deleteRows(mask.data()); + if (dev_run) basis.printScalars(); + + h.addRows(get_num, lower.data(), upper.data(), nnz, start.data(), + index.data(), value.data()); + h.run(); - printf("After deleting and adding row %1d, solve takes %d iterations and yields objective = %g\n", - int(use_row), int(info.simplex_iteration_count), info.objective_function_value); + if (dev_run) + printf( + "After deleting and adding %d rows in mask, solve takes %d iterations " + "and yields objective = %g\n", + int(get_num), int(info.simplex_iteration_count), + info.objective_function_value); + // REQUIRE(info.simplex_iteration_count < ieration_count0); } diff --git a/src/lp_data/Highs.cpp b/src/lp_data/Highs.cpp index eace21d527..8372d4aded 100644 --- a/src/lp_data/Highs.cpp +++ b/src/lp_data/Highs.cpp @@ -1268,13 +1268,14 @@ HighsStatus Highs::solve() { options_.solver == kHighsChooseString; const bool has_basis = basis_.useful; if (has_basis) { - assert(basis_.col_status.size() == static_cast(incumbent_lp.num_col_)); - assert(basis_.row_status.size() == static_cast(incumbent_lp.num_row_)); + assert(basis_.col_status.size() == + static_cast(incumbent_lp.num_col_)); + assert(basis_.row_status.size() == + static_cast(incumbent_lp.num_row_)); } if (basis_.valid) assert(basis_.useful); - if ((has_basis || options_.presolve == kHighsOffString || - unconstrained_lp) && + if ((has_basis || options_.presolve == kHighsOffString || unconstrained_lp) && solver_will_use_basis) { // There is a valid basis for the problem, presolve is off, or LP // has no constraint matrix, and the solver will use the basis diff --git a/src/lp_data/HighsInterface.cpp b/src/lp_data/HighsInterface.cpp index e7faea3860..627338c7e1 100644 --- a/src/lp_data/HighsInterface.cpp +++ b/src/lp_data/HighsInterface.cpp @@ -472,7 +472,7 @@ HighsStatus Highs::addRowsInterface(HighsInt ext_num_new_row, HighsBasis& basis = basis_; HighsScale& scale = lp.scale_; bool& useful_basis = basis.useful; - + bool& lp_has_scaling = lp.scale_.has_scaling; // Check that if nonzeros are to be added then the model has a positive number @@ -578,10 +578,9 @@ HighsStatus Highs::addRowsInterface(HighsInt ext_num_new_row, } void deleteBasisEntries(std::vector& status, - bool& deleted_basic, - bool& deleted_nonbasic, - const HighsIndexCollection& index_collection, - const HighsInt entry_dim) { + bool& deleted_basic, bool& deleted_nonbasic, + const HighsIndexCollection& index_collection, + const HighsInt entry_dim) { assert(ok(index_collection)); assert(static_cast(entry_dim) == status.size()); HighsInt from_k; @@ -603,11 +602,12 @@ void deleteBasisEntries(std::vector& status, // Account for the initial entries being kept if (k == from_k) new_num_entry = delete_from_entry; // Identify whether a basic or a nonbasic entry has been deleted - for (HighsInt entry = delete_from_entry; entry <= delete_to_entry; entry++) { + for (HighsInt entry = delete_from_entry; entry <= delete_to_entry; + entry++) { if (status[entry] == HighsBasisStatus::kBasic) { - deleted_basic = true; + deleted_basic = true; } else { - deleted_nonbasic = true; + deleted_nonbasic = true; } } if (delete_to_entry >= entry_dim - 1) break; @@ -620,30 +620,24 @@ void deleteBasisEntries(std::vector& status, status.resize(new_num_entry); } -void deleteBasisCols(HighsBasis& basis, - const HighsIndexCollection& index_collection, - const HighsInt original_num_col) { +void deleteBasisCols(HighsBasis& basis, + const HighsIndexCollection& index_collection, + const HighsInt original_num_col) { bool deleted_basic; bool deleted_nonbasic; - deleteBasisEntries(basis.col_status, - deleted_basic, - deleted_nonbasic, - index_collection, original_num_col); - if (deleted_basic) - basis.valid = false; + deleteBasisEntries(basis.col_status, deleted_basic, deleted_nonbasic, + index_collection, original_num_col); + if (deleted_basic) basis.valid = false; } -void deleteBasisRows(HighsBasis& basis, - const HighsIndexCollection& index_collection, - const HighsInt original_num_row) { +void deleteBasisRows(HighsBasis& basis, + const HighsIndexCollection& index_collection, + const HighsInt original_num_row) { bool deleted_basic; bool deleted_nonbasic; - deleteBasisEntries(basis.row_status, - deleted_basic, - deleted_nonbasic, - index_collection, original_num_row); - if (deleted_nonbasic) - basis.valid = false; + deleteBasisEntries(basis.row_status, deleted_basic, deleted_nonbasic, + index_collection, original_num_row); + if (deleted_nonbasic) basis.valid = false; } void Highs::deleteColsInterface(HighsIndexCollection& index_collection) { @@ -674,7 +668,7 @@ void Highs::deleteColsInterface(HighsIndexCollection& index_collection) { } else { assert(!basis.valid); } - + if (lp.scale_.has_scaling) { deleteScale(lp.scale_.col, index_collection); lp.scale_.col.resize(lp.num_col_); @@ -2044,13 +2038,13 @@ HighsStatus Highs::elasticityFilterReturn( run_status = this->deleteCols(original_num_col, lp.num_col_ - 1); assert(run_status == HighsStatus::kOk); - // + // // Now that deleteRows and deleteCols may yield a valid basis, the // lack of dual values triggers an assert in // getKktFailures. Ultimately (#2081) the dual values will be // available but, for now, make the basis invalid. basis_.valid = false; - + run_status = this->changeColsCost(0, original_num_col - 1, original_col_cost.data()); assert(run_status == HighsStatus::kOk); diff --git a/src/lp_data/HighsSolution.cpp b/src/lp_data/HighsSolution.cpp index 7dee62abe5..a28060861a 100644 --- a/src/lp_data/HighsSolution.cpp +++ b/src/lp_data/HighsSolution.cpp @@ -1357,8 +1357,7 @@ HighsStatus formSimplexLpBasisAndFactor(HighsLpSolverObject& solver_object, // If new scaling is performed, the hot start information is // no longer valid if (new_scaling) ekk_instance.clearHotStart(); - const bool check_basis = basis.alien || - (!basis.valid && basis.useful); + const bool check_basis = basis.alien || (!basis.valid && basis.useful); if (check_basis) { // The basis needs to be checked for rank deficiency, and possibly // completed if it is rectangular @@ -1616,9 +1615,11 @@ void HighsBasis::print(std::string message) const { if (!this->useful) return; this->printScalars(message); for (HighsInt iCol = 0; iCol < HighsInt(this->col_status.size()); iCol++) - printf("Basis: col_status[%2d] = %d\n", int(iCol), int(this->col_status[iCol])); + printf("Basis: col_status[%2d] = %d\n", int(iCol), + int(this->col_status[iCol])); for (HighsInt iRow = 0; iRow < HighsInt(this->row_status.size()); iRow++) - printf("Basis: row_status[%2d] = %d\n", int(iRow), int(this->row_status[iRow])); + printf("Basis: row_status[%2d] = %d\n", int(iRow), + int(this->row_status[iRow])); } void HighsBasis::printScalars(std::string message) const { diff --git a/src/simplex/HApp.h b/src/simplex/HApp.h index 8b63ce74b8..178593cbc6 100644 --- a/src/simplex/HApp.h +++ b/src/simplex/HApp.h @@ -136,14 +136,17 @@ inline HighsStatus solveLpSimplex(HighsLpSolverObject& solver_object) { // If new scaling is performed, the hot start information is // no longer valid if (new_scaling) ekk_instance.clearHotStart(); - // + // if (!status.has_basis && !basis.valid && basis.useful) { // There is no simplex basis, but there is a useful HiGHS basis // that is not validated - assert(basis.col_status.size() == static_cast(incumbent_lp.num_col_)); - assert(basis.row_status.size() == static_cast(incumbent_lp.num_row_)); + assert(basis.col_status.size() == + static_cast(incumbent_lp.num_col_)); + assert(basis.row_status.size() == + static_cast(incumbent_lp.num_row_)); HighsStatus return_status = formSimplexLpBasisAndFactor(solver_object); - if (return_status != HighsStatus::kOk) return returnFromSolveLpSimplex(solver_object, HighsStatus::kError); + if (return_status != HighsStatus::kOk) + return returnFromSolveLpSimplex(solver_object, HighsStatus::kError); // formSimplexLpBasisAndFactor may introduce variables with // HighsBasisStatus::kNonbasic, so refine it refineBasis(incumbent_lp, solution, basis);