From 49f320a14869f43e877591fec00803ef793f7e78 Mon Sep 17 00:00:00 2001 From: JAJHall Date: Mon, 15 Jul 2024 11:02:05 +0100 Subject: [PATCH] Added option mip_max_start_nodes to be used for mip_max_nodes when a MIP is solved to complete a partial solution to avoid excessive cost --- FEATURES.md | 3 ++- src/lp_data/Highs.cpp | 24 ++++++++++++++++-------- src/lp_data/HighsOptions.h | 11 ++++++++++- src/presolve/HighsSymmetry.cpp | 4 ++-- 4 files changed, 30 insertions(+), 12 deletions(-) diff --git a/FEATURES.md b/FEATURES.md index a5e198a407..c6db2f0380 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -8,6 +8,7 @@ Added `int64_t mip_total_lp_iterations` to `HighsCallbackDataOut` and modified a Introduced `const double kHighsUndefined` as value of undefined values in a user solution. It's equal to `kHighsInf` -Added `Highs::setSolution(const HighsInt num_entries, const HighsInt* index, const double* value);` to allow a sparse primal solution to be defined. +Added `Highs::setSolution(const HighsInt num_entries, const HighsInt* index, const double* value);` to allow a sparse primal solution to be defined. When a MIP is solved to do this, the value of (new) option `mip_max_start_nodes` is used for `mip_max_nodes` to avoid excessive cost + diff --git a/src/lp_data/Highs.cpp b/src/lp_data/Highs.cpp index ac8196715a..acf2780b39 100644 --- a/src/lp_data/Highs.cpp +++ b/src/lp_data/Highs.cpp @@ -3465,6 +3465,8 @@ HighsStatus Highs::completeSolutionFromDiscreteAssignment() { const HighsInt num_continuous_variable = lp.num_col_ - num_discrete_variable; assert(num_continuous_variable >= 0); bool call_run = true; + const bool few_fixed_discrete_variables = + 10 * num_fixed_discrete_variable < num_discrete_variable; if (num_unfixed_discrete_variable == 0) { // Solution is integer valued if (num_continuous_variable == 0) { @@ -3484,14 +3486,14 @@ HighsStatus Highs::completeSolutionFromDiscreteAssignment() { } } else { // There are unfixed discrete variables - if (10 * num_fixed_discrete_variable < num_discrete_variable) { - // Too few discrete variables are fixed - highsLogUser(options_.log_options, HighsLogType::kInfo, - "User-supplied values fix only %d / %d discrete variables, " - "so not attempting to complete a feasible solution\n", - int(num_fixed_discrete_variable), - int(num_discrete_variable)); - call_run = false; + if (few_fixed_discrete_variables) { + // Too few discrete variables are fixed so warn, but still + // attempt to complete a feasible solution + highsLogUser( + options_.log_options, HighsLogType::kWarning, + "User-supplied values fix only %d / %d discrete variables, " + "so attempt to complete a feasible solution may be expensive\n", + int(num_fixed_discrete_variable), int(num_discrete_variable)); } else { highsLogUser(options_.log_options, HighsLogType::kInfo, "Attempting to find feasible solution " @@ -3508,9 +3510,15 @@ HighsStatus Highs::completeSolutionFromDiscreteAssignment() { // feasible - or it's not worth using the user solution solution_.clear(); if (call_run) { + // Solve the model, using mip_max_start_nodes for + // mip_max_nodes... + const HighsInt mip_max_nodes = options_.mip_max_nodes; + options_.mip_max_nodes = options_.mip_max_start_nodes; // Solve the model basis_.clear(); return_status = this->run(); + // ... remembering to recover the original value of mip_max_nodes + options_.mip_max_nodes = mip_max_nodes; } // Recover the column bounds and integrality lp.col_lower_ = save_col_lower; diff --git a/src/lp_data/HighsOptions.h b/src/lp_data/HighsOptions.h index 9a9bb29aa6..7c939583ae 100644 --- a/src/lp_data/HighsOptions.h +++ b/src/lp_data/HighsOptions.h @@ -401,6 +401,7 @@ struct HighsOptionsStruct { bool mip_allow_restart; HighsInt mip_max_nodes; HighsInt mip_max_stall_nodes; + HighsInt mip_max_start_nodes; HighsInt mip_max_leaves; HighsInt mip_max_improving_sols; HighsInt mip_lp_age_limit; @@ -527,6 +528,7 @@ struct HighsOptionsStruct { mip_allow_restart(false), mip_max_nodes(0), mip_max_stall_nodes(0), + mip_max_start_nodes(0), mip_max_leaves(0), mip_max_improving_sols(0), mip_lp_age_limit(0), @@ -916,6 +918,13 @@ class HighsOptions : public HighsOptionsStruct { "MIP solver max number of nodes where estimate is above cutoff bound", advanced, &mip_max_stall_nodes, 0, kHighsIInf, kHighsIInf); records.push_back(record_int); + + record_int = new OptionRecordInt( + "mip_max_start_nodes", + "MIP solver max number of nodes when completing a partial MIP start", + advanced, &mip_max_start_nodes, 0, 500, kHighsIInf); + records.push_back(record_int); + #ifdef HIGHS_DEBUGSOL record_string = new OptionRecordString( "mip_debug_solution_file", @@ -944,7 +953,7 @@ class HighsOptions : public HighsOptionsStruct { records.push_back(record_string); record_int = new OptionRecordInt( - "mip_max_leaves", "MIP solver max number of leave nodes", advanced, + "mip_max_leaves", "MIP solver max number of leaf nodes", advanced, &mip_max_leaves, 0, kHighsIInf, kHighsIInf); records.push_back(record_int); diff --git a/src/presolve/HighsSymmetry.cpp b/src/presolve/HighsSymmetry.cpp index a34d1d7902..53f998ef45 100644 --- a/src/presolve/HighsSymmetry.cpp +++ b/src/presolve/HighsSymmetry.cpp @@ -1078,7 +1078,7 @@ bool HighsSymmetryDetection::distinguishVertex(HighsInt targetCell) { void HighsSymmetryDetection::backtrack(HighsInt backtrackStackNewEnd, HighsInt backtrackStackEnd) { - // we assume that we always backtrack from a leave node, i.e. a discrete + // we assume that we always backtrack from a leaf node, i.e. a discrete // partition therefore we do not need to remember the values of the hash // contributions as it is the indentity for each position and all new cells // are on the cell creation stack. @@ -1805,7 +1805,7 @@ void HighsSymmetryDetection::run(HighsSymmetries& symmetries) { // than the current best leave, because its prefix length is smaller // than the best leaves and it would have been already pruned if // it's certificate value was larger unless it is equal to the first - // leave nodes certificate value which is caught by the first case + // leaf nodes certificate value which is caught by the first case // of the if condition. Hence, having a lexicographically smaller // certificate value than the best leave is the only way to get // here.