Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 48 additions & 3 deletions check/TestPresolve.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -953,7 +953,7 @@ TEST_CASE("presolve-light", "[highs_test_presolve]") {
printf("%s presolved LP has %d columns; %d rows and %d nonzeros\n",
k == 0 ? "Fully" : "Lightly", int(presolved_num_col),
int(presolved_num_row), int(presolved_num_nz));
highs.setOptionValue("presolve_light", kHighsOnString);
highs.setOptionValue("presolve_mode", kPresolveModeLight);
}

highs.resetThreadScheduler(true);
Expand Down Expand Up @@ -1032,7 +1032,7 @@ TEST_CASE("presolve-initial-sweep-all", "[highs_test_presolve]") {
// Tests all cases in initial sweep
Highs highs;
highs.setOptionValue("output_flag", dev_run);
highs.setOptionValue("presolve_light", kHighsOnString);
highs.setOptionValue("presolve_mode", kPresolveModeLight);
if (dev_run) highs.setOptionValue("presolve_rule_logging", true);
HighsLp lp;
lp.num_col_ = 7;
Expand Down Expand Up @@ -1281,7 +1281,7 @@ TEST_CASE("presolve-light-no-crossover", "[highs_test_presolve]") {

REQUIRE(h.readModel(filename) == HighsStatus::kOk);

REQUIRE(h.setOptionValue("presolve_light", kHighsOnString) ==
REQUIRE(h.setOptionValue("presolve_mode", kPresolveModeLight) ==
HighsStatus::kOk);
REQUIRE(h.setOptionValue("solve_relaxation", true) == HighsStatus::kOk);
testAllLpSolversCrossoverChoose(h, HighsModelStatus::kOptimal);
Expand Down Expand Up @@ -2288,3 +2288,48 @@ TEST_CASE("hipo-ipx-pdlp-no-crossover", "[highs_test_presolve]") {
}

#endif

TEST_CASE("presolve-mode", "[highs_test_presolve]") {
std::string model_file =
std::string(HIGHS_DIR) + "/check/instances/egout-ac.mps";
Highs h;
h.setOptionValue("output_flag", dev_run);
REQUIRE(h.setOptionValue("presolve_rule_logging", true) == HighsStatus::kOk);
REQUIRE(h.readModel(model_file) == HighsStatus::kOk);

std::vector<std::string> presolve_modes = {"default", "initial sweep",
"light", "full"};
h.setOptionValue("", dev_run);

HighsInt initial_sweep_num_row = 0;
HighsInt initial_sweep_num_col = 0;
HighsInt initial_sweep_num_nz = 0;
const HighsLp& presolved_lp = h.getPresolvedLp();
for (HighsInt iMode = kPresolveModeMin; iMode <= kPresolveModeMax; iMode++) {
if (dev_run) {
std::string text = "Testing presolve mode " + std::to_string(iMode) +
" (" + presolve_modes[iMode] + ")";
highsPrintStringAsHeading(text);
}
REQUIRE(h.setOptionValue("presolve_mode", iMode) == HighsStatus::kOk);
REQUIRE(h.presolve() == HighsStatus::kOk);
if (dev_run)
printf("Presolved LP has %d rows, %d columns and %d nonzeros\n",
int(presolved_lp.num_row_), int(presolved_lp.num_col_),
int(presolved_lp.numNz()));
if (iMode == kPresolveModeDefault || iMode == kPresolveModeFull) {
REQUIRE(presolved_lp.num_row_ == 0);
REQUIRE(presolved_lp.num_col_ == 0);
REQUIRE(presolved_lp.numNz() == 0);
} else if (iMode == kPresolveModeInitialSweep) {
initial_sweep_num_row = presolved_lp.num_row_;
initial_sweep_num_col = presolved_lp.num_col_;
initial_sweep_num_nz = presolved_lp.numNz();
} else {
REQUIRE(initial_sweep_num_row > presolved_lp.num_row_);
REQUIRE(initial_sweep_num_col > presolved_lp.num_col_);
REQUIRE(initial_sweep_num_nz > presolved_lp.numNz());
}
}
h.resetThreadScheduler(true);
}
8 changes: 7 additions & 1 deletion docs/src/guide/further.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,19 @@ cases, the time saved by solving the reduced model is very much
greater than the time taken to perform presolve. Once the presolved
model is solved, a postsolve procedure (of minimal computational cost)
deduces the optimal solution to the original model. Hence presolve is
performed by default. The only exception occus when there is a valid
performed by default. The only exception occurs when there is a valid
basis for an LP and the simplex solver is used. In this case the
original LP is solved, starting from this basis. In cases where the
use of presolve is found not to be advantageous, its use can be
switched off by setting the [presolve](@ref option-presolve) option to
"off".

HiGHS has four presolve modes for LPs, selected by setting the value of the [presolve\_mode](@ref option-presolve-mode) option
* Default (`presolve_mode=0`): HiGHS chooses the appropriate presolve rules for the problem to be solved
* Initial sweep (`presolve_mode=1`): This mode removes variables that are fixed or have no coefficients in the constraint matrix, and constraints that have no coefficients in the constraint matrix, a single coefficient in the constraint matrix, or are satisfied by virtue of bounds on the constraint activity corresponding to bounds on the variables.
* Light (`presolve_mode=2`): This mode applies the "cheap" presolve rules
* Full (`presolve_mode=3`): This mode applies all presolve rules

HiGHS has a method [presolve](@ref Presolve/postsolve) that performs presolve on
the incumbent model, allowing the presolved model to be extracted or
written to a file. This is intended for users who have their own
Expand Down
25 changes: 18 additions & 7 deletions docs/src/options/definitions.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
- Default: "choose"

## [solver](@id option-solver)
- LP/QP solver: "choose", "simplex", "ipm", "ipx", "hipo", "pdlp", "hipdlp" or "qpasm"
- LP/QP solver: "choose", "simplex", "ipm", "ipx", "hipo", "pdlp", "qpasm" or "hipdlp",
- Type: string
- Default: "choose"

Expand Down Expand Up @@ -200,9 +200,9 @@
- Default: "false"

## [write\_solution\_style](@id option-write-solution-style)
- Style of solution file (raw = computer-readable, pretty = human-readable): -1 => HiGHS old raw (deprecated); 0 => HiGHS raw; 1 => HiGHS pretty; 2 => Glpsol raw; 3 => Glpsol pretty; 4 => HiGHS sparse raw
- Style of solution file (raw = computer-readable, pretty = human-readable): -1 => HiGHS old raw (deprecated); 0 => HiGHS raw; 1 => HiGHS pretty; 2 => Glpsol raw; 3 => Glpsol pretty; 4 => HiGHS sparse raw; 5 => HiGHS partial raw
- Type: integer
- Range: {-1, 4}
- Range: {-1, 5}
- Default: 0

## [glpsol\_cost\_row\_location](@id option-glpsol-cost-row-location)
Expand Down Expand Up @@ -246,6 +246,12 @@
- Type: string
- Default: ""

## [presolve\_mode](@id option-presolve-mode)
- Presolve mode 0 => default; 1 => initial sweep; 2 => light; 3 => full
- Type: integer
- Range: {0, 3}
- Default: 0

## [mip\_detect\_symmetry](@id option-mip-detect-symmetry)
- Whether MIP symmetry should be detected
- Type: boolean
Expand Down Expand Up @@ -445,13 +451,13 @@
- Default: "choose"

## [hipo\_parallel\_force](@id option-hipo-parallel-force)
- Bit map to force the use of parallel techniques in HiPO
- Bit mask to force parallel techniques in HiPO
- Type: integer
- Range: {0, 1023}
- Default: 0

## [hipo\_parallel\_forbid](@id option-hipo-parallel-forbid)
- Bit map to forbid the use of parallel techniques in HiPO
- Bit mask to forbid parallel techniques in HiPO
- Type: integer
- Range: {0, 1023}
- Default: 0
Expand All @@ -461,8 +467,8 @@
- Type: string
- Default: "choose"

## [hipo\_fatcor](@id option-hipo-factor)
- HiPO matrix factorisation: "choose", "multifrontal", or "uplooking"
## [hipo\_factor](@id option-hipo-factor)
- HiPO matrix factorisation: "choose", "multifrontal" or "uplooking"
- Type: string
- Default: "choose"

Expand Down Expand Up @@ -519,6 +525,11 @@
- Range: [1e-10, inf]
- Default: 1e-07

## [test\_qp\_oracle](@id option-test-qp-oracle)
- Use an oracle for the QP Hessian
- Type: boolean
- Default: "false"

## [qp\_allow\_hot\_start](@id option-qp-allow-hot-start)
- Allow the active set QP solver to hot start
- Type: boolean
Expand Down
12 changes: 11 additions & 1 deletion highs/lp_data/HConst.h
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,17 @@ enum class HighsBasisStatus : uint8_t {
// and postsolve
};

// Types of LP presolve rules
// Types of presolve modes
enum PresolveMode : uint8_t {
kPresolveModeMin = 0,
kPresolveModeDefault = kPresolveModeMin,
kPresolveModeInitialSweep,
kPresolveModeLight,
kPresolveModeFull,
kPresolveModeMax = kPresolveModeFull
};

// Types of presolve rules
enum PresolveRuleType : int {
kPresolveRuleIllegal = -1,
kPresolveRuleMin = 0,
Expand Down
3 changes: 0 additions & 3 deletions highs/lp_data/HighsOptions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -455,9 +455,6 @@ OptionStatus checkOptionValue(const HighsLogOptions& report_log_options,
} else if (option.name == kHipoFactorString) {
if (!optionHipoFactorOk(report_log_options, value))
return OptionStatus::kIllegalValue;
} else if (option.name == kPresolveLightString) {
if (!optionOffChooseOnOk(report_log_options, option.name, value))
return OptionStatus::kIllegalValue;
}
return OptionStatus::kOk;
}
Expand Down
46 changes: 23 additions & 23 deletions highs/lp_data/HighsOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,6 @@ const string kModelFileString = "model_file";
const string kReadBasisFileString = "read_basis_file";
const string kWriteBasisFileString = "write_basis_file";
const string kPresolveString = "presolve";
const string kPresolveLightString = "presolve_light";
const string kSolverString = "solver";
const string kParallelString = "parallel";
const string kThreadsString = "threads";
Expand Down Expand Up @@ -444,7 +443,6 @@ struct HighsOptionsStruct {
bool lp_presolve_requires_basis_postsolve;
bool mps_parser_type_free;
bool use_warm_start;
std::string presolve_light;
bool write_matrix_image;
bool write_hessian_image;
HighsInt keep_n_rows;
Expand All @@ -458,6 +456,7 @@ struct HighsOptionsStruct {
HighsInt max_dual_simplex_phase1_cleanup_level;
HighsInt simplex_price_strategy;
HighsInt simplex_unscaled_solution_strategy;
HighsInt presolve_mode;
HighsInt presolve_reduction_limit;
HighsInt restart_presolve_reduction_limit;
HighsInt presolve_substitution_maxfillin;
Expand Down Expand Up @@ -621,7 +620,6 @@ struct HighsOptionsStruct {
lp_presolve_requires_basis_postsolve(false),
mps_parser_type_free(false),
use_warm_start(true),
presolve_light(""),
write_matrix_image(false),
write_hessian_image(false),
keep_n_rows(0),
Expand All @@ -635,6 +633,7 @@ struct HighsOptionsStruct {
max_dual_simplex_phase1_cleanup_level(0),
simplex_price_strategy(0),
simplex_unscaled_solution_strategy(0),
presolve_mode(0),
presolve_reduction_limit(0),
restart_presolve_reduction_limit(0),
presolve_substitution_maxfillin(0),
Expand Down Expand Up @@ -1021,7 +1020,7 @@ class HighsOptions : public HighsOptionsStruct {
"pretty = human-readable): "
"-1 => HiGHS old raw (deprecated); 0 => HiGHS raw; "
"1 => HiGHS pretty; 2 => Glpsol raw; 3 => Glpsol pretty; "
"4 => HiGHS sparse raw",
"4 => HiGHS sparse raw; 5 => HiGHS partial raw",
advanced, &write_solution_style, kSolutionStyleMin, kSolutionStyleRaw,
kSolutionStyleMax);
records.push_back(record_int);
Expand Down Expand Up @@ -1110,6 +1109,13 @@ class HighsOptions : public HighsOptionsStruct {
&write_iis_model_file, kHighsFilenameDefault);
records.push_back(record_string);

record_int = new OptionRecordInt(
"presolve_mode",
"Presolve mode 0 => default; 1 => initial sweep; 2 => light; 3 => full",
advanced, &presolve_mode, kPresolveModeMin, kPresolveModeDefault,
kPresolveModeMax);
records.push_back(record_int);

record_bool = new OptionRecordBool(
"mip_detect_symmetry", "Whether MIP symmetry should be detected",
advanced, &mip_detect_symmetry, true);
Expand Down Expand Up @@ -1338,6 +1344,19 @@ class HighsOptions : public HighsOptionsStruct {
advanced, &hipo_parallel_type, kHighsChooseString);
records.push_back(record_string);

record_int = new OptionRecordInt(
"hipo_parallel_force", "Bit mask to force parallel techniques in HiPO",
advanced, &hipo_parallel_force, 0, 0,
static_cast<int>(hipo::ParallelTechnique::kMaxSum));
records.push_back(record_int);

record_int =
new OptionRecordInt("hipo_parallel_forbid",
"Bit mask to forbid parallel techniques in HiPO",
advanced, &hipo_parallel_forbid, 0, 0,
static_cast<int>(hipo::ParallelTechnique::kMaxSum));
records.push_back(record_int);

record_string = new OptionRecordString(
kHipoOrderingString,
"HiPO matrix reordering: \"choose\", \"metis\", \"amd\" or \"rcm\"",
Expand All @@ -1356,19 +1375,6 @@ class HighsOptions : public HighsOptionsStruct {
advanced, &hipo_block_size, 0, 128, kHighsIInf);
records.push_back(record_int);

record_int = new OptionRecordInt(
"hipo_parallel_force", "Bit mask to force parallel techniques in HiPO",
advanced, &hipo_parallel_force, 0, 0,
static_cast<int>(hipo::ParallelTechnique::kMaxSum));
records.push_back(record_int);

record_int =
new OptionRecordInt("hipo_parallel_forbid",
"Bit mask to forbid parallel techniques in HiPO",
advanced, &hipo_parallel_forbid, 0, 0,
static_cast<int>(hipo::ParallelTechnique::kMaxSum));
records.push_back(record_int);

record_bool = new OptionRecordBool(
"hipo_serial_blas",
"Force OpenBLAS to run in serial when used by HiPO. This setting is "
Expand Down Expand Up @@ -1524,12 +1530,6 @@ class HighsOptions : public HighsOptionsStruct {
advanced, &use_warm_start, true);
records.push_back(record_bool);

record_string = new OptionRecordString(
kPresolveLightString,
"Use only low-cost presolve rules: \"off\", \"choose\" or \"on\"",
advanced, &presolve_light, kHighsChooseString);
records.push_back(record_string);

record_bool = new OptionRecordBool(
"write_matrix_image",
"Write an image of the constraint matrix to a file", advanced,
Expand Down
Loading
Loading