Skip to content

Commit

Permalink
merge latest
Browse files Browse the repository at this point in the history
  • Loading branch information
galabovaa committed Jan 15, 2025
2 parents 34c6adf + 4c07e5d commit 5a34ed8
Show file tree
Hide file tree
Showing 19 changed files with 503 additions and 81 deletions.
2 changes: 2 additions & 0 deletions FEATURES.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,5 @@ Fixed bug in presolve when pointers stored in HighsMatrixSlice get invalidated w
Primal and dual residual tolerances - applied following IPM or PDLP solution - now documented as options

Highs::getCols (Highs::getRows) now runs in linear time if the internal constraint matrix is stored column-wise (row-wise). Added ensureColwise/Rowwise to the Highs class, the C API and highspy so that users can set the internal constraint matrix storage orientation

When columns and rows are deleted from the incumbent LP after a basic solution has been found, HiGHS no longer invalidates the basis. Now it maintains the basic and nonbasic status of the remaining variables and constraints. When the model is re-solved, this information is used to construct a starting basis.
2 changes: 1 addition & 1 deletion app/HighsRuntimeOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ bool loadOptions(const HighsLogOptions& report_log_options, int argc,
case HighsLoadOptionsStatus::kError:
return false;
case HighsLoadOptionsStatus::kEmpty:
writeOptionsToFile(stdout, options.records);
writeOptionsToFile(stdout, options.log_options, options.records);
return false;
default:
break;
Expand Down
1 change: 1 addition & 0 deletions app/RunHighs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ int main(int argc, char** argv) {
// settings defined in any options file.
highs.passOptions(loaded_options);
// highs.writeOptions("Options.md");
highs.writeOptions("", true);

// Load the model from model_file
HighsStatus read_status = highs.readModel(model_file);
Expand Down
201 changes: 201 additions & 0 deletions check/TestLpModification.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2274,3 +2274,204 @@ TEST_CASE("row-wise-get-row-avgas", "[highs_data]") {
testAvgasGetRow(h);
testAvgasGetCol(h);
}

TEST_CASE("hot-start-after-delete", "[highs_data]") {
Highs h;
h.setOptionValue("output_flag", dev_run);
const HighsLp& lp = h.getLp();
const HighsInfo& info = h.getInfo();
const HighsBasis& basis = h.getBasis();
const HighsSolution& solution = h.getSolution();
std::string model = "avgas";
std::string model_file =
std::string(HIGHS_DIR) + "/check/instances/" + model + ".mps";
h.readModel(model_file);
h.run();
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);

HighsInt max_dim = std::max(lp.num_col_, lp.num_row_);
std::vector<double> cost(1);
std::vector<double> lower(1);
std::vector<double> upper(1);
HighsInt nnz;
std::vector<HighsInt> start(1);
std::vector<HighsInt> index(max_dim);
std::vector<double> 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<HighsInt> 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<HighsInt> 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());

h.run();
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();
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);
}
8 changes: 4 additions & 4 deletions check/TestOptions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ TEST_CASE("internal-options", "[highs_options]") {
REQUIRE(options.small_matrix_value == 0.001);
REQUIRE(options.mps_parser_type_free);

if (dev_run) reportOptions(stdout, options.records, true);
if (dev_run) reportOptions(stdout, report_log_options, options.records, true);

return_status = checkOptions(report_log_options, options.records);
REQUIRE(return_status == OptionStatus::kOk);
Expand Down Expand Up @@ -157,7 +157,7 @@ TEST_CASE("internal-options", "[highs_options]") {

if (dev_run) {
printf("\nAfter setting allowed_matrix_scale_factor to 1\n");
reportOptions(stdout, options.records);
reportOptions(stdout, report_log_options, options.records);
}

double allowed_matrix_scale_factor_double = 1e-7;
Expand All @@ -174,7 +174,7 @@ TEST_CASE("internal-options", "[highs_options]") {

if (dev_run) {
printf("\nAfter testing HighsInt options\n");
reportOptions(stdout, options.records);
reportOptions(stdout, report_log_options, options.records);
}

// Check setting double options
Expand Down Expand Up @@ -231,7 +231,7 @@ TEST_CASE("internal-options", "[highs_options]") {
options.log_options, options.records, model_file);
REQUIRE(return_status == OptionStatus::kUnknownOption);

if (dev_run) reportOptions(stdout, options.records);
if (dev_run) reportOptions(stdout, report_log_options, options.records);

bool get_mps_parser_type_free;
return_status =
Expand Down
6 changes: 3 additions & 3 deletions src/Highs.h
Original file line number Diff line number Diff line change
Expand Up @@ -312,11 +312,11 @@ class Highs {

/**
* @brief Write (deviations from default values of) the options to a
* file, with the extension ".html" producing HTML, otherwise using
* the standard format used to read options from a file.
* file, using the standard format used to read options from a file.
* Possible to write only deviations from default values.
*/
HighsStatus writeOptions(const std::string& filename, //!< The filename
const bool report_only_deviations = false) const;
const bool report_only_deviations = false);

/**
* @brief Returns the number of user-settable options
Expand Down
13 changes: 13 additions & 0 deletions src/lp_data/HStruct.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,14 +58,27 @@ struct HotStart {
};

struct HighsBasis {
// Logical flags for a HiGHS basis:
//
// valid: has been factored by HiGHS
//
// alien: a basis that's been set externally, so cannot be assumed
// to even have the right number of basic and nonbasic variables
//
// useful: a basis that may be useful
//
// Need useful since, by default, a basis is alien but not useful
bool valid = false;
bool alien = true;
bool useful = false;
bool was_alien = true;
HighsInt debug_id = -1;
HighsInt debug_update_count = -1;
std::string debug_origin_name = "None";
std::vector<HighsBasisStatus> col_status;
std::vector<HighsBasisStatus> row_status;
void print(std::string message = "") const;
void printScalars(std::string message = "") const;
void invalidate();
void clear();
};
Expand Down
Loading

0 comments on commit 5a34ed8

Please sign in to comment.