diff --git a/check/TestLpModification.cpp b/check/TestLpModification.cpp index c5c1b45506..efc212a327 100644 --- a/check/TestLpModification.cpp +++ b/check/TestLpModification.cpp @@ -809,7 +809,10 @@ TEST_CASE("LP-modification", "[highs_data]") { col1357_upper[2] = 0; col1357_upper[3] = 0; - REQUIRE(highs.changeColsBounds(col1357_num_ix, col1357_col_set, col1357_lower, + // Doing it with indices out of order is fine + HighsInt col5713_col_set[] = {5, 7, 1, 3}; + + REQUIRE(highs.changeColsBounds(col1357_num_ix, col5713_col_set, col1357_lower, col1357_upper) == HighsStatus::kOk); callRun(highs, options.log_options, "highs.run()", HighsStatus::kOk); @@ -864,6 +867,29 @@ TEST_CASE("LP-modification", "[highs_data]") { callRun(highs, options.log_options, "highs.run()", HighsStatus::kOk); + // Change row bounds again but with indices out of order + HighsInt row7890135_row_set[] = {7, 8, 9, 0, 1, 3, 5}; + row0135789_lower[0] = local_lp.row_lower_[7]; + row0135789_lower[1] = local_lp.row_lower_[8]; + row0135789_lower[2] = local_lp.row_lower_[9]; + row0135789_lower[3] = local_lp.row_lower_[0]; + row0135789_lower[4] = local_lp.row_lower_[1]; + row0135789_lower[5] = local_lp.row_lower_[3]; + row0135789_lower[6] = local_lp.row_lower_[5]; + row0135789_upper[0] = local_lp.row_lower_[7]; + row0135789_upper[1] = local_lp.row_lower_[8]; + row0135789_upper[2] = local_lp.row_lower_[9]; + row0135789_upper[3] = local_lp.row_lower_[0]; + row0135789_upper[4] = local_lp.row_lower_[1]; + row0135789_upper[5] = local_lp.row_lower_[3]; + row0135789_upper[6] = local_lp.row_lower_[5]; + + REQUIRE(highs.changeRowsBounds(row0135789_num_ix, row7890135_row_set, + row0135789_lower, + row0135789_upper) == HighsStatus::kOk); + + callRun(highs, options.log_options, "highs.run()", HighsStatus::kOk); + REQUIRE(highs.deleteRows(0, num_row - 1) == HighsStatus::kOk); callRun(highs, options.log_options, "highs.run()", HighsStatus::kOk); @@ -926,6 +952,12 @@ TEST_CASE("LP-modification", "[highs_data]") { REQUIRE(highs.changeColsCost(col1357_num_ix, col1357_col_set, col1357_cost) == HighsStatus::kOk); + // Do it again but with indices out of order + double col5713_cost[] = {2.51, 2.71, 2.01, 2.31}; + + REQUIRE(highs.changeColsCost(col1357_num_ix, col5713_col_set, col5713_cost) == + HighsStatus::kOk); + callRun(highs, options.log_options, "highs.run()", HighsStatus::kOk); // Attempting to set row bounds with infinite lower bound returns error diff --git a/highs/Highs.h b/highs/Highs.h index 51c59115f5..956034d3be 100644 --- a/highs/Highs.h +++ b/highs/Highs.h @@ -1704,16 +1704,34 @@ class Highs { HighsStatus changeObjectiveOffsetInterface(const double ext_offset); HighsStatus changeIntegralityInterface(HighsIndexCollection& index_collection, const HighsVarType* usr_inegrality); + // Interface to change costs in a general (and safe) way HighsStatus changeCostsInterface(HighsIndexCollection& index_collection, const double* usr_col_cost); + // Interface to change costs without some safety checks, + // but with potential performance boosts + HighsStatus changeCostsInterfaceUnchecked( + HighsIndexCollection& index_collection, + std::vector& usr_col_cost); bool feasibleWrtBounds(const bool columns = true) const; + // Interface to change column bounds in a general (and safe) way HighsStatus changeColBoundsInterface(HighsIndexCollection& index_collection, const double* usr_col_lower, const double* usr_col_upper); + // Interface to change column bounds without some safety checks, + // but with potential performance boosts + HighsStatus changeColBoundsInterfaceUnchecked( + HighsIndexCollection& index_collection, + std::vector& usr_col_lower, std::vector& usr_col_upper); + // Interface to change row bounds in a general (and safe) way HighsStatus changeRowBoundsInterface(HighsIndexCollection& index_collection, const double* usr_row_lower, const double* usr_row_upper); + // Interface to change row bounds without some safety checks, + // but with potential performance boosts + HighsStatus changeRowBoundsInterfaceUnchecked( + HighsIndexCollection& index_collection, + std::vector& usr_row_lower, std::vector& usr_row_upper); void changeCoefficientInterface(const HighsInt ext_row, const HighsInt ext_col, const double ext_new_value); diff --git a/highs/lp_data/Highs.cpp b/highs/lp_data/Highs.cpp index 74403945cb..f5316ac2b2 100644 --- a/highs/lp_data/Highs.cpp +++ b/highs/lp_data/Highs.cpp @@ -2897,8 +2897,11 @@ HighsStatus Highs::changeColsCost(const HighsInt num_set_entries, return analyseSetCreateError(options_.log_options, "changeColsCost", create_error, true, num_set_entries, local_set.data(), model_.lp_.num_col_); + // Since we have already done the safety checks that would take place in + // changeCostsInterface and created local copies of cost, we can + // use the "unchecked" version HighsStatus call_status = - changeCostsInterface(index_collection, local_cost.data()); + changeCostsInterfaceUnchecked(index_collection, local_cost); HighsStatus return_status = HighsStatus::kOk; return_status = interpretCallStatus(options_.log_options, call_status, return_status, "changeCosts"); @@ -2976,8 +2979,11 @@ HighsStatus Highs::changeColsBounds(const HighsInt num_set_entries, return analyseSetCreateError(options_.log_options, "changeColsBounds", create_error, true, num_set_entries, local_set.data(), model_.lp_.num_col_); - HighsStatus call_status = changeColBoundsInterface( - index_collection, local_lower.data(), local_upper.data()); + // Since we have already done the safety checks that would take place in + // changeColBoundsInterface and created local copies of lower/upper, we can + // use the "unchecked" version + HighsStatus call_status = changeColBoundsInterfaceUnchecked( + index_collection, local_lower, local_upper); HighsStatus return_status = HighsStatus::kOk; return_status = interpretCallStatus(options_.log_options, call_status, return_status, "changeColBounds"); @@ -3057,8 +3063,11 @@ HighsStatus Highs::changeRowsBounds(const HighsInt num_set_entries, return analyseSetCreateError(options_.log_options, "changeRowsBounds", create_error, true, num_set_entries, local_set.data(), model_.lp_.num_row_); - HighsStatus call_status = changeRowBoundsInterface( - index_collection, local_lower.data(), local_upper.data()); + // Since we have already done the safety checks that would take place in + // changeRowBoundsInterface and created local copies of lower/upper, we can + // use the "unchecked" version + HighsStatus call_status = changeRowBoundsInterfaceUnchecked( + index_collection, local_lower, local_upper); HighsStatus return_status = HighsStatus::kOk; return_status = interpretCallStatus(options_.log_options, call_status, return_status, "changeRowBounds"); diff --git a/highs/lp_data/HighsInterface.cpp b/highs/lp_data/HighsInterface.cpp index 007605940f..371a1904b9 100644 --- a/highs/lp_data/HighsInterface.cpp +++ b/highs/lp_data/HighsInterface.cpp @@ -912,16 +912,21 @@ HighsStatus Highs::changeCostsInterface(HighsIndexCollection& index_collection, return HighsStatus::kError; // Take a copy of the cost that can be normalised std::vector local_colCost{cost, cost + num_cost}; + return changeCostsInterfaceUnchecked(index_collection, local_colCost); +} + +HighsStatus Highs::changeCostsInterfaceUnchecked( + HighsIndexCollection& index_collection, std::vector& cost) { HighsStatus return_status = HighsStatus::kOk; bool local_has_infinite_cost = false; return_status = interpretCallStatus( options_.log_options, - assessCosts(options_, 0, index_collection, local_colCost, - local_has_infinite_cost, options_.infinite_cost), + assessCosts(options_, 0, index_collection, cost, local_has_infinite_cost, + options_.infinite_cost), return_status, "assessCosts"); if (return_status == HighsStatus::kError) return return_status; HighsLp& lp = model_.lp_; - changeLpCosts(lp, index_collection, local_colCost, options_.infinite_cost); + changeLpCosts(lp, index_collection, cost, options_.infinite_cost); // Interpret possible introduction of infinite costs lp.has_infinite_cost_ = lp.has_infinite_cost_ || local_has_infinite_cost; @@ -976,16 +981,23 @@ HighsStatus Highs::changeColBoundsInterface( sortSetData(index_collection.set_num_entries_, index_collection.set_, col_lower, col_upper, NULL, local_colLower.data(), local_colUpper.data(), NULL); + return changeColBoundsInterfaceUnchecked(index_collection, local_colLower, + local_colUpper); +} + +HighsStatus Highs::changeColBoundsInterfaceUnchecked( + HighsIndexCollection& index_collection, std::vector& col_lower, + std::vector& col_upper) { HighsStatus return_status = HighsStatus::kOk; return_status = interpretCallStatus( options_.log_options, - assessBounds(options_, "col", 0, index_collection, local_colLower, - local_colUpper, options_.infinite_bound), + assessBounds(options_, "col", 0, index_collection, col_lower, col_upper, + options_.infinite_bound), return_status, "assessBounds"); if (return_status == HighsStatus::kError) return return_status; HighsLp& lp = model_.lp_; - changeLpColBounds(lp, index_collection, local_colLower, local_colUpper); + changeLpColBounds(lp, index_collection, col_lower, col_upper); // Update HiGHS basis status and (any) simplex move status of // nonbasic variables whose bounds have changed setNonbasicStatusInterface(index_collection, true); @@ -1027,16 +1039,23 @@ HighsStatus Highs::changeRowBoundsInterface( sortSetData(index_collection.set_num_entries_, index_collection.set_, lower, upper, NULL, local_rowLower.data(), local_rowUpper.data(), NULL); + return changeRowBoundsInterfaceUnchecked(index_collection, local_rowLower, + local_rowUpper); +} + +HighsStatus Highs::changeRowBoundsInterfaceUnchecked( + HighsIndexCollection& index_collection, std::vector& lower, + std::vector& upper) { HighsStatus return_status = HighsStatus::kOk; - return_status = interpretCallStatus( - options_.log_options, - assessBounds(options_, "row", 0, index_collection, local_rowLower, - local_rowUpper, options_.infinite_bound), - return_status, "assessBounds"); + return_status = + interpretCallStatus(options_.log_options, + assessBounds(options_, "row", 0, index_collection, + lower, upper, options_.infinite_bound), + return_status, "assessBounds"); if (return_status == HighsStatus::kError) return return_status; HighsLp& lp = model_.lp_; - changeLpRowBounds(lp, index_collection, local_rowLower, local_rowUpper); + changeLpRowBounds(lp, index_collection, lower, upper); // Update HiGHS basis status and (any) simplex move status of // nonbasic variables whose bounds have changed setNonbasicStatusInterface(index_collection, false);