Skip to content

Commit

Permalink
Merge pull request #1843 from ERGO-Code/fix-1808
Browse files Browse the repository at this point in the history
Add facility to specify a partial primal solution via indices and values
  • Loading branch information
jajhall committed Jul 14, 2024
2 parents 11e5385 + 3c34353 commit 08646bc
Show file tree
Hide file tree
Showing 11 changed files with 263 additions and 46 deletions.
7 changes: 7 additions & 0 deletions FEATURES.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,10 @@

Added `int64_t mip_total_lp_iterations` to `HighsCallbackDataOut` and modified accessor function

`Highs::writeSolution` and `Highs::writeBasis` now being done via `HighsIO` logging, so can be redirected to logging callback.

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.


6 changes: 3 additions & 3 deletions 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 Expand Up @@ -277,8 +277,8 @@ TEST_CASE("highs-callback-solution-basis-logging", "[highs-callback]") {
highs.run();
highs.setCallback(userInterruptCallback, p_user_callback_data);
highs.startCallback(kCallbackLogging);
highs.writeSolution("", kSolutionStylePretty);
highs.writeBasis("");
if (dev_run) highs.writeSolution("", kSolutionStylePretty);
if (dev_run) highs.writeBasis("");
}

TEST_CASE("highs-callback-simplex-interrupt", "[highs-callback]") {
Expand Down
51 changes: 51 additions & 0 deletions check/TestCheckSolution.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,57 @@ TEST_CASE("check-set-mip-solution", "[highs_check_solution]") {
highs.clear();
}

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++;

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);
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++;
}
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);
highs.run();
REQUIRE(info.mip_node_count < scratch_num_nodes);
highs.clear();
}
assert(other_tests);
std::remove(solution_file.c_str());
}

Expand Down
6 changes: 6 additions & 0 deletions src/Highs.h
Original file line number Diff line number Diff line change
Expand Up @@ -1063,6 +1063,12 @@ class Highs {
*/
HighsStatus setSolution(const HighsSolution& solution);

/**
* @brief Pass a sparse primal solution
*/
HighsStatus setSolution(const HighsInt num_entries, const HighsInt* index,
const double* value);

/**
* @brief Set the callback method to use for HiGHS
*/
Expand Down
21 changes: 20 additions & 1 deletion src/highs_bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,24 @@ HighsStatus highs_deleteRows(Highs* h, HighsInt num_set_entries, std::vector<Hig
return h->deleteRows(num_set_entries, indices.data());
}


HighsStatus highs_setSolution(Highs* h, HighsSolution& solution) {
return h->setSolution(solution);
}

HighsStatus highs_setSparseSolution(Highs* h, HighsInt num_entries,
py::array_t<HighsInt> index,
py::array_t<double> value) {
py::buffer_info index_info = index.request();
py::buffer_info value_info = value.request();

HighsInt* index_ptr = reinterpret_cast<HighsInt*>(index_info.ptr);
double* value_ptr = static_cast<double*>(value_info.ptr);

return h->setSolution(num_entries, index_ptr, value_ptr);
}


HighsStatus highs_setBasis(Highs* h, HighsBasis& basis) {
return h->setBasis(basis);
}
Expand Down Expand Up @@ -935,7 +953,8 @@ PYBIND11_MODULE(_core, m) {
.def("deleteCols", &highs_deleteCols)
.def("deleteVars", &highs_deleteCols) // alias
.def("deleteRows", &highs_deleteRows)
.def("setSolution", &Highs::setSolution)
.def("setSolution", &highs_setSolution)
.def("setSolution", &highs_setSparseSolution)
.def("setBasis", &highs_setBasis)
.def("setBasis", &highs_setLogicalBasis)
.def("modelStatusToString", &Highs::modelStatusToString)
Expand Down
5 changes: 5 additions & 0 deletions src/interfaces/highs_c_api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -668,6 +668,11 @@ HighsInt Highs_setSolution(void* highs, const double* col_value,
return (HighsInt)((Highs*)highs)->setSolution(solution);
}

HighsInt Highs_setSparseSolution(void* highs, const HighsInt num_entries,
const HighsInt* index, const double* value) {
return (HighsInt)((Highs*)highs)->setSolution(num_entries, index, value);
}

HighsInt Highs_setCallback(void* highs, HighsCCallbackType user_callback,
void* user_callback_data) {
auto status = static_cast<Highs*>(highs)->setCallback(user_callback,
Expand Down
13 changes: 13 additions & 0 deletions src/interfaces/highs_c_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -1139,6 +1139,19 @@ HighsInt Highs_setSolution(void* highs, const double* col_value,
const double* row_value, const double* col_dual,
const double* row_dual);

/**
* Set a partial primal solution by passing values for a set of variables
*
* @param highs A pointer to the Highs instance.
* @param num_entries Number of variables in the set
* @param index Indices of variables in the set
* @param value Values of variables in the set
*
* @returns A `kHighsStatus` constant indicating whether the call succeeded.
*/
HighsInt Highs_setSparseSolution(void* highs, const HighsInt num_entries,
const HighsInt* index, const double* value);

/**
* 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
1 change: 1 addition & 0 deletions src/lp_data/HStruct.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ struct HighsSolution {
std::vector<double> col_dual;
std::vector<double> row_value;
std::vector<double> row_dual;
bool hasUndefined();
void invalidate();
void clear();
};
Expand Down
Loading

0 comments on commit 08646bc

Please sign in to comment.