Skip to content

Commit

Permalink
Now clearing solution when completeSolutionFromDiscreteAssignment dec…
Browse files Browse the repository at this point in the history
…ides it's not worth it
  • Loading branch information
jajhall committed Jul 14, 2024
1 parent 8d269a2 commit 7fd28a1
Show file tree
Hide file tree
Showing 5 changed files with 178 additions and 60 deletions.
2 changes: 1 addition & 1 deletion check/TestCallbacks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
#include "catch.hpp"
#include "lp_data/HighsCallback.h"

const bool dev_run = true;
const bool dev_run = false;

const double egout_optimal_objective = 568.1007;
const double egout_objective_target = 610;
Expand Down
33 changes: 23 additions & 10 deletions check/TestCheckSolution.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
#include "SpecialLps.h"
#include "catch.hpp"

const bool dev_run = true;
const bool dev_run = false;

void runWriteReadCheckSolution(Highs& highs, const std::string model,
const HighsModelStatus require_model_status,
Expand Down Expand Up @@ -89,7 +89,7 @@ TEST_CASE("check-set-mip-solution", "[highs_check_solution]") {

highs.clear();

const bool other_tests = false;//true;
const bool other_tests = true;
const bool test0 = other_tests;
bool valid, integral, feasible;
if (test0) {
Expand Down Expand Up @@ -229,36 +229,49 @@ TEST_CASE("check-set-mip-solution", "[highs_check_solution]") {
highs.clear();
}

const bool test6 = true;//other_tests;
const bool test6 = other_tests;
if (test6) {
if (dev_run)
printf(
"\n***************************\nSolving from sparse integer "
"solution\n");
HighsInt num_integer_variable = 0;
for (HighsInt iCol = 0; iCol < lp.num_col_; iCol++)
if (lp.integrality_[iCol] == HighsVarType::kInteger) num_integer_variable++;
if (lp.integrality_[iCol] == HighsVarType::kInteger)
num_integer_variable++;


highs.setOptionValue("output_flag", dev_run);
highs.readModel(model_file);
std::vector<HighsInt> index;
std::vector<double> value;
// Check that duplicate values are spotted
index.push_back(0);
value.push_back(0);
index.push_back(1);
value.push_back(1);
index.push_back(0);
value.push_back(2);
HighsInt num_entries = index.size();
return_status = highs.setSolution(num_entries, index.data(), value.data());
REQUIRE(return_status == HighsStatus::kWarning);

index.clear();
value.clear();
std::vector<bool> is_set;
is_set.assign(lp.num_col_, false);
std::vector<HighsInt> index;
std::vector<double> value;
HighsInt num_to_set = std::max(10, (8*num_integer_variable)/10);
assert(num_to_set>0);
HighsInt num_to_set = 2;
assert(num_to_set > 0);
HighsRandom random;
for (HighsInt iSet = 0; iSet < num_to_set;) {
HighsInt iCol = random.integer(lp.num_col_);
if (lp.integrality_[iCol] != HighsVarType::kInteger) continue;
if (is_set[iCol]) continue;
is_set[iCol] = true;
index.push_back(iCol);
value.push_back(optimal_solution.col_value[iCol]);
iSet++;
}
HighsInt num_entries = index.size();
num_entries = index.size();
assert(num_entries == num_to_set);
return_status = highs.setSolution(num_entries, index.data(), value.data());
REQUIRE(return_status == HighsStatus::kOk);
Expand Down
5 changes: 2 additions & 3 deletions src/Highs.h
Original file line number Diff line number Diff line change
Expand Up @@ -1066,9 +1066,8 @@ class Highs {
/**
* @brief Pass a sparse primal solution
*/
HighsStatus setSolution(const HighsInt num_entries,
const HighsInt* index,
const double* value);
HighsStatus setSolution(const HighsInt num_entries, const HighsInt* index,
const double* value);

/**
* @brief Set the callback method to use for HiGHS
Expand Down
1 change: 1 addition & 0 deletions src/lp_data/HConst.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const size_t kHighsSize_tInf = std::numeric_limits<size_t>::max();
const HighsInt kHighsIInf = std::numeric_limits<HighsInt>::max();
const HighsInt kHighsIInf32 = std::numeric_limits<int>::max();
const double kHighsInf = std::numeric_limits<double>::infinity();
const double kHighsUndefined = kHighsInf;
const double kHighsTiny = 1e-14;
const double kHighsMacheps = std::ldexp(1, -52);
const double kHighsZero = 1e-50;
Expand Down
197 changes: 151 additions & 46 deletions src/lp_data/Highs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1966,11 +1966,53 @@ HighsStatus Highs::setSolution(const HighsSolution& solution) {
}

HighsStatus Highs::setSolution(const HighsInt num_entries,
const HighsInt* index,
const double* value) {
const HighsInt* index, const double* value) {
HighsStatus return_status = HighsStatus::kOk;
return_status = HighsStatus::kError;
return returnFromHighs(return_status);
// Warn about duplicates in index
HighsInt num_duplicates = 0;
std::vector<bool> is_set;
is_set.assign(model_.lp_.num_col_, false);
for (HighsInt iX = 0; iX < num_entries; iX++) {
HighsInt iCol = index[iX];
if (iCol < 0 || iCol > model_.lp_.num_col_) {
highsLogUser(options_.log_options, HighsLogType::kError,
"setSolution: User solution index %d has value %d out of "
"range [0, %d)",
int(iX), int(iCol), int(model_.lp_.num_col_));
return HighsStatus::kError;
} else if (value[iX] < model_.lp_.col_lower_[iCol] -
options_.primal_feasibility_tolerance ||
model_.lp_.col_upper_[iCol] +
options_.primal_feasibility_tolerance <
value[iX]) {
highsLogUser(options_.log_options, HighsLogType::kError,
"setSolution: User solution value %d of %g is infeasible "
"for bounds [%g, %g]",
int(iX), value[iX], model_.lp_.col_lower_[iCol],
model_.lp_.col_upper_[iCol]);
return HighsStatus::kError;
}
if (is_set[iCol]) num_duplicates++;
is_set[iCol] = true;
}
if (num_duplicates > 0) {
highsLogUser(options_.log_options, HighsLogType::kWarning,
"setSolution: User set of indices has %d duplicate%s: last "
"value used\n",
int(num_duplicates), num_duplicates > 1 ? "s" : "");
return_status = HighsStatus::kWarning;
}

// Clear the solution, indicate the values not determined by the
// user, and insert the values determined by the user
HighsSolution new_solution;
new_solution.col_value.assign(model_.lp_.num_col_, kHighsUndefined);
for (HighsInt iX = 0; iX < num_entries; iX++) {
HighsInt iCol = index[iX];
new_solution.col_value[iCol] = value[iX];
}
return interpretCallStatus(options_.log_options, setSolution(new_solution),
return_status, "setSolution");
}

HighsStatus Highs::setCallback(HighsCallbackFunctionType user_callback,
Expand Down Expand Up @@ -3356,62 +3398,125 @@ void Highs::invalidateEkk() { ekk_instance_.invalidate(); }

HighsStatus Highs::completeSolutionFromDiscreteAssignment() {
// Determine whether the current solution of a MIP is feasible and,
// if not, try to assign values to continuous variables to achieve a
// feasible solution. Valuable in the case where users make a
// heuristic assignment of discrete variables
// if not, try to assign values to continuous variables and discrete
// variables not at integer values to achieve a feasible
// solution. Valuable in the case where users make a heuristic
// (partial) assignment of discrete variables
assert(model_.isMip() && solution_.value_valid);
HighsLp& lp = model_.lp_;
bool valid, integral, feasible;
// Determine whether this solution is feasible, or just integer feasible
HighsStatus return_status = assessLpPrimalSolution(options_, lp, solution_,
valid, integral, feasible);
assert(return_status != HighsStatus::kError);
assert(valid);
// If the current solution is feasible, then solution can be used by
// MIP solver to get a primal bound
if (feasible) return HighsStatus::kOk;
// Determine whether the solution contains undefined values, in
// order to decide whether to check its feasibility
bool contains_undefined_values = false;
for (HighsInt iCol = 0; iCol < lp.num_col_; iCol++) {
if (solution_.col_value[iCol] == kHighsUndefined) {
contains_undefined_values = true;
break;
}
}
if (!contains_undefined_values) {
bool valid, integral, feasible;
// Determine whether this solution is integer feasible
HighsStatus return_status = assessLpPrimalSolution(
options_, lp, solution_, valid, integral, feasible);
assert(return_status != HighsStatus::kError);
assert(valid);
// If the current solution is integer feasible, then it can be
// used by MIP solver to get a primal bound
if (feasible) return HighsStatus::kOk;
}
// Save the column bounds and integrality in preparation for fixing
// the non-continuous variables when user-supplied values are
// the discrete variables when user-supplied values are
// integer
std::vector<double> save_col_lower = lp.col_lower_;
std::vector<double> save_col_upper = lp.col_upper_;
std::vector<HighsVarType> save_integrality = lp.integrality_;
const bool have_integrality = (lp.integrality_.size() != 0);
bool is_integer = true;
assert(have_integrality);
// Count the number of fixed and unfixed discrete variables
HighsInt num_fixed_discrete_variable = 0;
HighsInt num_unfixed_discrete_variable = 0;
for (HighsInt iCol = 0; iCol < lp.num_col_; iCol++) {
if (lp.integrality_[iCol] == HighsVarType::kContinuous) continue;
// Fix non-continuous variable if it has integer value
const double primal = solution_.col_value[iCol];
const double lower = lp.col_lower_[iCol];
const double upper = lp.col_upper_[iCol];
const HighsVarType type =
have_integrality ? lp.integrality_[iCol] : HighsVarType::kContinuous;
double col_infeasibility = 0;
double integer_infeasibility = 0;
assessColPrimalSolution(options_, primal, lower, upper, type,
col_infeasibility, integer_infeasibility);
if (integer_infeasibility > options_.mip_feasibility_tolerance) {
// Variable is not integer feasible, so record that a MIP will
// have to be solved
is_integer = false;
// Default value is lower bound, unless primal is integer for a
// discrete variable
solution_.col_value[iCol] = lp.col_lower_[iCol];
if (lp.integrality_[iCol] == HighsVarType::kContinuous) continue;
// Fix discrete variable if its value is defined and integer
if (primal == kHighsUndefined) {
num_unfixed_discrete_variable++;
} else {
// Variable is integer feasible, so fix it at this value and
// remove its integrality
lp.col_lower_[iCol] = solution_.col_value[iCol];
lp.col_upper_[iCol] = solution_.col_value[iCol];
lp.integrality_[iCol] = HighsVarType::kContinuous;
const double lower = lp.col_lower_[iCol];
const double upper = lp.col_upper_[iCol];
const HighsVarType type =
have_integrality ? lp.integrality_[iCol] : HighsVarType::kContinuous;
double col_infeasibility = 0;
double integer_infeasibility = 0;
assessColPrimalSolution(options_, primal, lower, upper, type,
col_infeasibility, integer_infeasibility);
if (integer_infeasibility > options_.mip_feasibility_tolerance) {
num_unfixed_discrete_variable++;
} else {
// Variable is integer feasible, so fix it at this value and
// remove its integrality
num_fixed_discrete_variable++;
lp.col_lower_[iCol] = primal;
lp.col_upper_[iCol] = primal;
lp.integrality_[iCol] = HighsVarType::kContinuous;
}
}
}
// If the solution is integer valued, only an LP needs to be solved,
// so clear all integrality
if (is_integer) lp.integrality_.clear();
const HighsInt num_discrete_variable =
num_unfixed_discrete_variable + num_fixed_discrete_variable;
const HighsInt num_continuous_variable = lp.num_col_ - num_discrete_variable;
assert(num_continuous_variable >= 0);
bool call_run = true;
if (num_unfixed_discrete_variable == 0) {
// Solution is integer valued
if (num_continuous_variable == 0) {
// There are no continuous variables, so no feasible solution can be
// deduced
highsLogUser(options_.log_options, HighsLogType::kInfo,
"User-supplied values of discrete variables cannot yield "
"feasible solution\n");
call_run = false;
} else {
// Solve an LP, so clear all integrality
lp.integrality_.clear();
highsLogUser(
options_.log_options, HighsLogType::kInfo,
"Attempting to find feasible solution "
"by solving LP for user-supplied values of discrete variables\n");
}
} 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;
} else {
highsLogUser(options_.log_options, HighsLogType::kInfo,
"Attempting to find feasible solution "
"by solving MIP for user-supplied values of %d / %d "
"discrete variables\n",
int(num_fixed_discrete_variable),
int(num_discrete_variable));
}
}
HighsStatus return_status = HighsStatus::kOk;
// Clear the current solution since either the user solution has
// been used to fix (a subset of) discrete variables - so a valid
// solution will be obtained from run() if the local model is
// feasible - or it's not worth using the user solution
solution_.clear();
basis_.clear();
// Solve the model
highsLogUser(options_.log_options, HighsLogType::kInfo,
"Attempting to find feasible solution "
"for (partial) user-supplied values of discrete variables\n");
return_status = this->run();
if (call_run) {
// Solve the model
basis_.clear();
return_status = this->run();
}
// Recover the column bounds and integrality
lp.col_lower_ = save_col_lower;
lp.col_upper_ = save_col_upper;
Expand Down

0 comments on commit 7fd28a1

Please sign in to comment.