Skip to content

Commit 671ff75

Browse files
committed
Incorporated integral scaling into mipIsKnapsack
1 parent 6f6333c commit 671ff75

File tree

5 files changed

+63
-26
lines changed

5 files changed

+63
-26
lines changed

check/TestMipSolver.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1011,9 +1011,11 @@ TEST_CASE("knapsack", "[highs_test_mip_solver]") {
10111011
const double offset = -100;
10121012
std::vector<double> neg_value(num_item);
10131013
std::vector<double> neg_weight(num_item);
1014+
std::vector<double> half_weight(num_item);
10141015
for (HighsInt iItem = 0; iItem < num_item; iItem++) {
10151016
neg_value[iItem] = -value[iItem];
10161017
neg_weight[iItem] = -weight[iItem];
1018+
half_weight[iItem] = 0.5 * weight[iItem];
10171019
}
10181020
HighsLp lp;
10191021
lp.sense_ = ObjSense::kMaximize;
@@ -1076,6 +1078,34 @@ TEST_CASE("knapsack", "[highs_test_mip_solver]") {
10761078
REQUIRE(h.passModel(lp) == HighsStatus::kOk);
10771079
REQUIRE(h.run() == HighsStatus::kOk);
10781080
REQUIRE(h.getModelStatus() == HighsModelStatus::kOptimal);
1081+
REQUIRE(h.getInfo().objective_function_value == required_objective_value);
1082+
1083+
lp.a_matrix_.value_ = half_weight;
1084+
lp.row_upper_ = {0.5 * capacity};
1085+
REQUIRE(h.passModel(lp) == HighsStatus::kOk);
1086+
REQUIRE(h.run() == HighsStatus::kOk);
1087+
REQUIRE(h.getModelStatus() == HighsModelStatus::kOptimal);
1088+
REQUIRE(h.getInfo().objective_function_value == required_objective_value);
1089+
1090+
lp.a_matrix_.value_ = {1.1, 2.25, 3.05, 6.25, 7, 4.125};
1091+
lp.row_upper_ = {capacity};
1092+
required_objective_value = -190;
1093+
REQUIRE(h.passModel(lp) == HighsStatus::kOk);
1094+
REQUIRE(h.run() == HighsStatus::kOk);
1095+
REQUIRE(h.getModelStatus() == HighsModelStatus::kOptimal);
1096+
REQUIRE(h.getInfo().objective_function_value == required_objective_value);
1097+
1098+
lp.a_matrix_.value_ = {1.1334, 2.2501, 3.0534, 6.251, 7, 4.125};
1099+
REQUIRE(h.passModel(lp) == HighsStatus::kOk);
1100+
REQUIRE(h.run() == HighsStatus::kOk);
1101+
REQUIRE(h.getModelStatus() == HighsModelStatus::kOptimal);
1102+
REQUIRE(h.getInfo().objective_function_value == required_objective_value);
1103+
1104+
lp.a_matrix_.value_[1] = 2.250001;
1105+
REQUIRE(h.passModel(lp) == HighsStatus::kOk);
1106+
REQUIRE(h.run() == HighsStatus::kOk);
1107+
REQUIRE(h.getModelStatus() == HighsModelStatus::kOptimal);
1108+
REQUIRE(h.getInfo().objective_function_value == required_objective_value);
10791109

10801110
h.resetGlobalScheduler(true);
10811111
}

highs/mip/HighsMipSolver.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,10 +91,10 @@ void HighsMipSolver::run() {
9191
// Determine whether this is a knapsack problem and, if so, at least
9292
// update the data on knapsack sub-MIPs
9393
HighsInt capacity = 0;
94-
if (mipdata_->mipIsKnapsack(capacity)) {
94+
if (mipdata_->mipIsKnapsack()) {
9595
mipdata_->knapsack_data_.num_problem++;
9696
mipdata_->knapsack_data_.sum_variables += orig_model_->num_col_;
97-
mipdata_->knapsack_data_.sum_capacity += capacity;
97+
mipdata_->knapsack_data_.sum_capacity += mipdata_->knapsack_capacity_;
9898
// Solve as a knapsack
9999
HighsStatus call_status = mipdata_->heuristics.solveMipKnapsack();
100100
assert(call_status == HighsStatus::kOk);

highs/mip/HighsMipSolverData.cpp

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -685,6 +685,8 @@ void HighsMipSolverData::init() {
685685
optimality_limit = mipsolver.options_mip_->objective_bound;
686686
primal_dual_integral.initialise();
687687
knapsack_data_.initialise();
688+
knapsack_capacity_ = 0;
689+
knapsack_integral_scale_ = 0;
688690

689691
if (mipsolver.options_mip_->mip_report_level == 0)
690692
dispfreq = 0;
@@ -2651,7 +2653,7 @@ void HighsMipSolverData::callbackUserSolution(
26512653
}
26522654
}
26532655

2654-
bool HighsMipSolverData::mipIsKnapsack(HighsInt& capacity) {
2656+
bool HighsMipSolverData::mipIsKnapsack(const bool logging) {
26552657
const HighsLp& lp = *(mipsolver.model_);
26562658
// Has to have one constraint
26572659
if (lp.num_row_ != 1) return false;
@@ -2662,18 +2664,24 @@ bool HighsMipSolverData::mipIsKnapsack(HighsInt& capacity) {
26622664
return false;
26632665
const bool upper = lp.row_upper_[0] < kHighsInf;
26642666
const HighsInt constraint_sign = upper ? 1 : -1;
2665-
// Now check that all the (signed) coefficients are integer and non-negative
2666-
for (HighsInt iEl = 0; iEl < lp.a_matrix_.numNz(); iEl++) {
2667-
double coeff = constraint_sign * lp.a_matrix_.value_[iEl];
2668-
if (coeff < 0) return false;
2669-
if (fractionality(coeff) > 0) return false;
2670-
}
2667+
// Now check that all the (signed) coefficients are non-negative
2668+
for (HighsInt iEl = 0; iEl < lp.a_matrix_.numNz(); iEl++)
2669+
if (constraint_sign * lp.a_matrix_.value_[iEl] < 0) return false;
2670+
this->knapsack_integral_scale_ =
2671+
HighsIntegers::integralScale(lp.a_matrix_.value_, 1e-6, 1e-6);
2672+
if (this->knapsack_integral_scale_ == 0) return false;
2673+
if (this->knapsack_integral_scale_ > 1000000) return false;
2674+
if (logging)
2675+
highsLogUser(mipsolver.options_mip_->log_options, HighsLogType::kInfo,
2676+
"MIP is a knapsack problem with with scale %d\n",
2677+
int(this->knapsack_integral_scale_));
26712678
// Capacity must be integer, but OK to round down any fractional
26722679
// values since activity of constraint is integer
26732680
double double_capacity =
26742681
upper ? lp.row_upper_[0] : constraint_sign * lp.row_lower_[0];
2682+
double_capacity *= this->knapsack_integral_scale_;
26752683
const double capacity_margin = 1e-6;
2676-
capacity = std::floor(double_capacity + capacity_margin);
2684+
this->knapsack_capacity_ = std::floor(double_capacity + capacity_margin);
26772685
// Problem is knapsack!
26782686
return true;
26792687
}

highs/mip/HighsMipSolverData.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,8 @@ struct HighsMipSolverData {
162162
HighsDebugSol debugSolution;
163163

164164
HighsKnapsackData knapsack_data_;
165+
HighsInt knapsack_capacity_;
166+
HighsInt knapsack_integral_scale_;
165167

166168
HighsMipSolverData(HighsMipSolver& mipsolver)
167169
: mipsolver(mipsolver),
@@ -220,7 +222,9 @@ struct HighsMipSolverData {
220222
upper_bound(kHighsInf),
221223
upper_limit(kHighsInf),
222224
optimality_limit(kHighsInf),
223-
debugSolution(mipsolver) {
225+
debugSolution(mipsolver),
226+
knapsack_capacity_(0),
227+
knapsack_integral_scale_(0) {
224228
domain.addCutpool(cutpool);
225229
domain.addConflictPool(conflictPool);
226230
}
@@ -309,7 +313,7 @@ struct HighsMipSolverData {
309313
void callbackUserSolution(
310314
const double mipsolver_objective_value,
311315
const userMipSolutionCallbackOrigin user_solution_callback_origin);
312-
bool mipIsKnapsack(HighsInt& capacity);
316+
bool mipIsKnapsack(const bool logging = true);
313317
};
314318

315319
#endif

highs/mip/HighsPrimalHeuristics.cpp

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1750,10 +1750,7 @@ HighsStatus HighsPrimalHeuristics::solveMipKnapsackReturn(
17501750
const HighsStatus& return_status) {
17511751
const HighsLp& lp = *(mipsolver.model_);
17521752
std::stringstream ss;
1753-
if (!mipsolver.submip) {
1754-
ss.str(std::string());
1755-
ss << "MIP is a knapsack problem: ";
1756-
}
1753+
if (!mipsolver.submip) ss.str(std::string());
17571754
if (mipsolver.modelstatus_ == HighsModelStatus::kOptimal) {
17581755
// mipsolver.solution_objective_ is the objective value for the
17591756
// original problem - using the offset and ignoring the
@@ -1771,12 +1768,12 @@ HighsStatus HighsPrimalHeuristics::solveMipKnapsackReturn(
17711768
mipsolver.gap_ = 0;
17721769
if (!mipsolver.submip)
17731770
ss << highsFormatToString(
1774-
"optimal objective by dynamic programming is %g",
1771+
"Optimal objective by dynamic programming is %g",
17751772
mipsolver.solution_objective_);
17761773
} else {
17771774
if (!mipsolver.submip)
17781775
ss << highsFormatToString(
1779-
"model status is %s",
1776+
"Model status is %s",
17801777
utilModelStatusToString(mipsolver.modelstatus_).c_str());
17811778
mipsolver.solution_.clear();
17821779
}
@@ -1789,16 +1786,13 @@ HighsStatus HighsPrimalHeuristics::solveMipKnapsackReturn(
17891786
HighsStatus HighsPrimalHeuristics::solveMipKnapsack() {
17901787
const HighsLp& lp = *(mipsolver.model_);
17911788
const HighsLogOptions& log_options = mipsolver.options_mip_->log_options;
1792-
HighsInt capacity_;
1793-
assert(mipsolver.mipdata_->mipIsKnapsack(capacity_));
1789+
assert(mipsolver.mipdata_->mipIsKnapsack(false));
17941790

17951791
const bool upper = lp.row_upper_[0] < kHighsInf;
17961792
const HighsInt constraint_sign = upper ? 1 : -1;
1797-
double double_capacity =
1798-
upper ? lp.row_upper_[0] : constraint_sign * lp.row_lower_[0];
1799-
const double capacity_margin = 1e-6;
1800-
const HighsInt capacity = std::floor(double_capacity + capacity_margin);
1801-
1793+
const HighsInt integral_scale = mipsolver.mipdata_->knapsack_integral_scale_;
1794+
assert(integral_scale > 0);
1795+
const HighsInt capacity = mipsolver.mipdata_->knapsack_capacity_;
18021796
if (capacity < 0) {
18031797
mipsolver.modelstatus_ = HighsModelStatus::kInfeasible;
18041798
return solveMipKnapsackReturn(HighsStatus::kOk);
@@ -1816,7 +1810,8 @@ HighsStatus HighsPrimalHeuristics::solveMipKnapsack() {
18161810
for (HighsInt iCol = 0; iCol < lp.num_col_; iCol++) {
18171811
for (HighsInt iEl = lp.a_matrix_.start_[iCol];
18181812
iEl < lp.a_matrix_.start_[iCol + 1]; iEl++)
1819-
weight[iCol] = HighsInt(constraint_sign * lp.a_matrix_.value_[iEl]);
1813+
weight[iCol] = HighsInt(integral_scale *
1814+
(constraint_sign * lp.a_matrix_.value_[iEl]));
18201815
}
18211816
HighsInt sense = HighsInt(lp.sense_);
18221817
// Set up the values for the knapsack solver. Since it solves a

0 commit comments

Comments
 (0)