Skip to content

Commit 64101a4

Browse files
committed
Neater knapsack logging in MIP solving report; formatted
1 parent b9b74de commit 64101a4

File tree

5 files changed

+106
-84
lines changed

5 files changed

+106
-84
lines changed

check/TestMipSolver.cpp

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1024,7 +1024,7 @@ TEST_CASE("knapsack", "[highs_test_mip_solver]") {
10241024
lp.col_lower_.assign(lp.num_col_, 0);
10251025
lp.col_upper_.assign(lp.num_col_, 1);
10261026
lp.integrality_.assign(lp.num_col_, HighsVarType::kInteger);
1027-
lp.row_lower_ = {-kHighsInf};
1027+
lp.row_lower_ = {-kHighsInf};
10281028
lp.row_upper_ = {capacity};
10291029
lp.a_matrix_.format_ = MatrixFormat::kRowwise;
10301030
lp.a_matrix_.start_ = {0, 6};
@@ -1038,30 +1038,28 @@ TEST_CASE("knapsack", "[highs_test_mip_solver]") {
10381038
for (int sense = 0; sense < 2; sense++) {
10391039
for (int sign = 0; sign < 2; sign++) {
10401040
if (sense == 0) {
1041-
lp.sense_ = ObjSense::kMaximize;
1042-
lp.col_cost_ = value;
1043-
required_objective_value = 0;
1041+
lp.sense_ = ObjSense::kMaximize;
1042+
lp.col_cost_ = value;
1043+
required_objective_value = 0;
10441044
} else {
1045-
lp.sense_ = ObjSense::kMinimize;
1046-
lp.col_cost_ = neg_value;
1047-
required_objective_value = 2.0e0 * offset;
1045+
lp.sense_ = ObjSense::kMinimize;
1046+
lp.col_cost_ = neg_value;
1047+
required_objective_value = 2.0e0 * offset;
10481048
}
10491049
if (sign == 0) {
1050-
lp.a_matrix_.value_ = weight;
1051-
lp.row_lower_ = {-kHighsInf};
1052-
lp.row_upper_ = {capacity};
1050+
lp.a_matrix_.value_ = weight;
1051+
lp.row_lower_ = {-kHighsInf};
1052+
lp.row_upper_ = {capacity};
10531053
} else {
1054-
lp.a_matrix_.value_ = neg_weight;
1055-
lp.row_lower_ = {-capacity};
1056-
lp.row_upper_ = {kHighsInf};
1054+
lp.a_matrix_.value_ = neg_weight;
1055+
lp.row_lower_ = {-capacity};
1056+
lp.row_upper_ = {kHighsInf};
10571057
}
10581058
REQUIRE(h.passModel(lp) == HighsStatus::kOk);
10591059
REQUIRE(h.run() == HighsStatus::kOk);
10601060
REQUIRE(h.getModelStatus() == HighsModelStatus::kOptimal);
10611061
REQUIRE(h.getInfo().objective_function_value == required_objective_value);
10621062
}
1063-
}
1063+
}
10641064
h.resetGlobalScheduler(true);
10651065
}
1066-
1067-

highs/lp_data/HighsLp.cpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ bool HighsLp::isBip() const {
3131
assert(integrality_size == this->num_col_);
3232
for (HighsInt iCol = 0; iCol < this->num_col_; iCol++) {
3333
if (this->integrality_[iCol] == HighsVarType::kContinuous) return false;
34-
if (this->col_lower_[iCol] != 0 || this->col_upper_[iCol] != 1) return false;
34+
if (this->col_lower_[iCol] != 0 || this->col_upper_[iCol] != 1)
35+
return false;
3536
}
3637
}
3738
return true;
@@ -43,7 +44,8 @@ bool HighsLp::isKnapsack(HighsInt& capacity) const {
4344
// Has to be a binary integer programming problem
4445
if (!this->isBip()) return false;
4546
// Must be one-sided constraint
46-
if (this->row_lower_[0] > -kHighsInf && this->row_upper_[0] < kHighsInf) return false;
47+
if (this->row_lower_[0] > -kHighsInf && this->row_upper_[0] < kHighsInf)
48+
return false;
4749
const bool upper = this->row_upper_[0] < kHighsInf;
4850
const HighsInt constraint_sign = upper ? 1 : -1;
4951
// Now check that all the (signed) coefficients are integer and non-negative
@@ -54,9 +56,10 @@ bool HighsLp::isKnapsack(HighsInt& capacity) const {
5456
}
5557
// Capacity must be integer, but OK to round down any fractional
5658
// values since activity of constraint is integer
57-
double double_capacity = upper ? this->row_upper_[0] : constraint_sign*this->row_lower_[0];
59+
double double_capacity =
60+
upper ? this->row_upper_[0] : constraint_sign * this->row_lower_[0];
5861
const double capacity_margin = 1e-6;
59-
capacity = std::floor(double_capacity+capacity_margin);
62+
capacity = std::floor(double_capacity + capacity_margin);
6063
// Problem is knapsack!
6164
return true;
6265
}

highs/mip/HighsMipSolver.cpp

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -865,14 +865,17 @@ void HighsMipSolver::cleanupSolve(const bool mip_logging) {
865865
(long long unsigned)mipdata_->sepa_lp_iterations,
866866
(long long unsigned)mipdata_->heuristic_lp_iterations);
867867
const HighsKnapsackData& knapsack_data = this->mipdata_->knapsack_data_;
868-
highsLogUser(options_mip_->log_options, HighsLogType::kInfo,
869-
" Knapsack MIPs %d\n", knapsack_data.num_problem);
868+
std::stringstream ss;
869+
ss.str(std::string());
870+
ss << highsFormatToString(" Knapsack MIPs %d",
871+
int(knapsack_data.num_problem));
870872
if (knapsack_data.num_problem > 0)
871-
highsLogUser(options_mip_->log_options, HighsLogType::kInfo,
872-
" Mean var count %d\n Mean capacity %d\n",
873-
int((1.0 * knapsack_data.sum_variables) / knapsack_data.num_problem),
874-
int((1.0 * knapsack_data.sum_capacity) / knapsack_data.num_problem));
875-
873+
ss << highsFormatToString(
874+
" (mean items %d; mean capacity %d)",
875+
int((1.0 * knapsack_data.sum_variables) / knapsack_data.num_problem),
876+
int((1.0 * knapsack_data.sum_capacity) / knapsack_data.num_problem));
877+
highsLogUser(options_mip_->log_options, HighsLogType::kInfo, "%s\n",
878+
ss.str().c_str());
876879
if (!timeless_log) analysis_.reportMipTimer();
877880

878881
assert(modelstatus_ != HighsModelStatus::kNotset);

highs/mip/HighsMipSolverData.cpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2798,5 +2798,3 @@ void HighsKnapsackData::add(const HighsKnapsackData& knapsack_data) {
27982798
this->sum_variables += knapsack_data.sum_variables;
27992799
this->sum_capacity += knapsack_data.sum_capacity;
28002800
}
2801-
2802-

highs/mip/HighsPrimalHeuristics.cpp

Lines changed: 75 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
#include "io/HighsIO.h"
1515
#include "lp_data/HConst.h"
1616
#include "lp_data/HighsLpUtils.h"
17-
#include "lp_data/HighsModelUtils.h" // For utilModelStatusToString
17+
#include "lp_data/HighsModelUtils.h" // For utilModelStatusToString
1818
#include "mip/HighsCutGeneration.h"
1919
#include "mip/HighsDomainChange.h"
2020
#include "mip/HighsLpRelaxation.h"
@@ -1649,49 +1649,57 @@ void HighsPrimalHeuristics::flushStatistics() {
16491649
}
16501650

16511651
double knapsackRecurrence(const HighsInt num_item,
1652-
const std::vector<double>& value,
1653-
const std::vector<HighsInt>& weight,
1654-
const double capacity,
1655-
std::vector<std::vector<double>> &dp_result,
1656-
std::vector<std::vector<bool>> &use_item) {
1657-
if (num_item == 0 || capacity == 0)
1658-
return 0; // Base case
1652+
const std::vector<double>& value,
1653+
const std::vector<HighsInt>& weight,
1654+
const double capacity,
1655+
std::vector<std::vector<double>>& dp_result,
1656+
std::vector<std::vector<bool>>& use_item) {
1657+
if (num_item == 0 || capacity == 0) return 0; // Base case
16591658

1660-
if (dp_result[num_item][capacity] != -1) return dp_result[num_item][capacity]; // Check if result is already computed
1659+
if (dp_result[num_item][capacity] != -1)
1660+
return dp_result[num_item]
1661+
[capacity]; // Check if result is already computed
16611662

16621663
// Exclude the item
1663-
double exclude = knapsackRecurrence(num_item-1, value, weight, capacity, dp_result, use_item);
1664+
double exclude = knapsackRecurrence(num_item - 1, value, weight, capacity,
1665+
dp_result, use_item);
16641666

16651667
// Include the item (if it fits in the knapsack)
16661668
double include = 0;
1667-
if (weight[num_item-1] <= capacity)
1668-
include = value[num_item-1] + knapsackRecurrence(num_item-1, value, weight, capacity - weight[num_item-1], dp_result, use_item);
1669+
if (weight[num_item - 1] <= capacity)
1670+
include = value[num_item - 1] +
1671+
knapsackRecurrence(num_item - 1, value, weight,
1672+
capacity - weight[num_item - 1], dp_result,
1673+
use_item);
16691674

16701675
// Store whether the item is used with this capacity
16711676
use_item[num_item][capacity] = include > exclude;
16721677
// Store the result
1673-
dp_result[num_item][capacity] = use_item[num_item][capacity] ? include : exclude;
1674-
1675-
return dp_result[num_item][capacity];
1678+
dp_result[num_item][capacity] =
1679+
use_item[num_item][capacity] ? include : exclude;
16761680

1681+
return dp_result[num_item][capacity];
16771682
}
16781683

16791684
HighsModelStatus solveKnapsack(const HighsLogOptions& log_options,
1680-
const HighsInt num_item,
1681-
const std::vector<double>& value,
1682-
const std::vector<HighsInt>& weight,
1683-
const HighsInt capacity,
1684-
double& solution_objective,
1685-
std::vector<double>& solution) {
1685+
const HighsInt num_item,
1686+
const std::vector<double>& value,
1687+
const std::vector<HighsInt>& weight,
1688+
const HighsInt capacity,
1689+
double& solution_objective,
1690+
std::vector<double>& solution) {
16861691
assert(capacity > 0);
1687-
1692+
16881693
// Set up the DP result array, indicating that no optimal objective
16891694
// values are known
1690-
std::vector<std::vector<double>> dp_result(num_item + 1, std::vector<double>(capacity + 1, -1));
1695+
std::vector<std::vector<double>> dp_result(
1696+
num_item + 1, std::vector<double>(capacity + 1, -1));
16911697
// Set up the item use array, indicating that items are not used
1692-
std::vector<std::vector<bool>> use_item(num_item + 1, std::vector<bool>(capacity + 1, false));
1698+
std::vector<std::vector<bool>> use_item(
1699+
num_item + 1, std::vector<bool>(capacity + 1, false));
16931700

1694-
solution_objective = knapsackRecurrence(num_item, value, weight, capacity, dp_result, use_item);
1701+
solution_objective = knapsackRecurrence(num_item, value, weight, capacity,
1702+
dp_result, use_item);
16951703

16961704
// Deduce the solution
16971705
std::vector<HighsInt> knapsack_solution(num_item, 0);
@@ -1708,15 +1716,17 @@ HighsModelStatus solveKnapsack(const HighsLogOptions& log_options,
17081716
}
17091717
const HighsInt capacity_violation = std::max(0, -capacity_slack);
17101718
if (capacity_violation > 0) {
1711-
highsLogUser(log_options, HighsLogType::kError,
1712-
"HighsPrimalHeuristics::solveKnapsack() Capacity violation is (%d)\n",
1713-
int(capacity_violation));
1714-
return HighsModelStatus::kSolveError;
1719+
highsLogUser(
1720+
log_options, HighsLogType::kError,
1721+
"HighsPrimalHeuristics::solveKnapsack() Capacity violation is (%d)\n",
1722+
int(capacity_violation));
1723+
return HighsModelStatus::kSolveError;
17151724
}
17161725
return HighsModelStatus::kOptimal;
17171726
}
17181727

1719-
HighsStatus HighsPrimalHeuristics::solveMipKnapsackReturn(const HighsStatus& return_status) {
1728+
HighsStatus HighsPrimalHeuristics::solveMipKnapsackReturn(
1729+
const HighsStatus& return_status) {
17201730
const HighsLp& lp = *(mipsolver.model_);
17211731
std::stringstream ss;
17221732
if (!mipsolver.submip) {
@@ -1730,24 +1740,28 @@ HighsStatus HighsPrimalHeuristics::solveMipKnapsackReturn(const HighsStatus& ret
17301740
// relate to the problem within the MIP solver. This is a
17311741
// minimization without offset
17321742
HighsInt sense = HighsInt(lp.sense_);
1733-
double mipsolver_objective = sense * mipsolver.solution_objective_ - lp.offset_;
1743+
double mipsolver_objective =
1744+
sense * mipsolver.solution_objective_ - lp.offset_;
17341745
mipsolver.bound_violation_ = 0;
17351746
mipsolver.integrality_violation_ = 0;
17361747
mipsolver.row_violation_ = 0;
17371748
mipsolver.mipdata_->lower_bound = mipsolver_objective;
17381749
mipsolver.mipdata_->upper_bound = mipsolver_objective;
17391750
mipsolver.gap_ = 0;
17401751
if (!mipsolver.submip)
1741-
ss << highsFormatToString("optimal objective by dynamic programming is %g",
1742-
mipsolver.solution_objective_);
1752+
ss << highsFormatToString(
1753+
"optimal objective by dynamic programming is %g",
1754+
mipsolver.solution_objective_);
17431755
} else {
17441756
if (!mipsolver.submip)
1745-
ss << highsFormatToString("model status is %s",
1746-
utilModelStatusToString(mipsolver.modelstatus_).c_str());
1757+
ss << highsFormatToString(
1758+
"model status is %s",
1759+
utilModelStatusToString(mipsolver.modelstatus_).c_str());
17471760
mipsolver.solution_.clear();
17481761
}
1749-
if (!mipsolver.submip) highsLogUser(mipsolver.options_mip_->log_options, HighsLogType::kInfo,
1750-
"\n%s\n", ss.str().c_str());
1762+
if (!mipsolver.submip)
1763+
highsLogUser(mipsolver.options_mip_->log_options, HighsLogType::kInfo,
1764+
"\n%s\n", ss.str().c_str());
17511765
return return_status;
17521766
}
17531767

@@ -1757,13 +1771,14 @@ HighsStatus HighsPrimalHeuristics::solveMipKnapsack() {
17571771
const HighsLogOptions& log_options = mipsolver.options_mip_->log_options;
17581772
HighsInt capacity_;
17591773
assert(lp.isKnapsack(capacity_));
1760-
1774+
17611775
const bool upper = lp.row_upper_[0] < kHighsInf;
17621776
const HighsInt constraint_sign = upper ? 1 : -1;
1763-
double double_capacity = upper ? lp.row_upper_[0] : constraint_sign * lp.row_lower_[0];
1777+
double double_capacity =
1778+
upper ? lp.row_upper_[0] : constraint_sign * lp.row_lower_[0];
17641779
const double capacity_margin = 1e-6;
1765-
const HighsInt capacity = std::floor(double_capacity+capacity_margin);
1766-
1780+
const HighsInt capacity = std::floor(double_capacity + capacity_margin);
1781+
17671782
if (capacity < 0) {
17681783
mipsolver.modelstatus_ = HighsModelStatus::kInfeasible;
17691784
return solveMipKnapsackReturn(HighsStatus::kOk);
@@ -1779,45 +1794,50 @@ HighsStatus HighsPrimalHeuristics::solveMipKnapsack() {
17791794
std::vector<HighsInt> weight(lp.num_col_, 0);
17801795
assert(lp.a_matrix_.format_ == MatrixFormat::kColwise);
17811796
for (HighsInt iCol = 0; iCol < lp.num_col_; iCol++) {
1782-
for (HighsInt iEl = lp.a_matrix_.start_[iCol]; iEl < lp.a_matrix_.start_[iCol+1]; iEl++)
1797+
for (HighsInt iEl = lp.a_matrix_.start_[iCol];
1798+
iEl < lp.a_matrix_.start_[iCol + 1]; iEl++)
17831799
weight[iCol] = HighsInt(constraint_sign * lp.a_matrix_.value_[iEl]);
17841800
}
17851801
HighsInt sense = HighsInt(lp.sense_);
17861802
// Set up the values for the knapsack solver. Since it solves a
17871803
// maximization problem, have to negate the costs if MIP is a
1788-
// minimization
1804+
// minimization
17891805
std::vector<double> value;
17901806
for (HighsInt iCol = 0; iCol < lp.num_col_; iCol++)
17911807
value.push_back(-sense * lp.col_cost_[iCol]);
17921808
// Solve the knapsack problem by DP
17931809
double knapsack_optimal_objective_value;
17941810
std::vector<double>& solution = mipsolver.solution_;
1795-
mipsolver.modelstatus_ = solveKnapsack(log_options,
1796-
lp.num_col_, value, weight, capacity,
1797-
knapsack_optimal_objective_value, solution);
1811+
mipsolver.modelstatus_ =
1812+
solveKnapsack(log_options, lp.num_col_, value, weight, capacity,
1813+
knapsack_optimal_objective_value, solution);
17981814
if (mipsolver.modelstatus_ != HighsModelStatus::kOptimal)
17991815
return solveMipKnapsackReturn(HighsStatus::kError);
18001816

18011817
// Get the objective value corresponding to the original problem
1802-
const double solution_objective = lp.offset_ - sense * knapsack_optimal_objective_value;
1818+
const double solution_objective =
1819+
lp.offset_ - sense * knapsack_optimal_objective_value;
18031820

18041821
// Compute the objective directly as a check
18051822
double check_objective = lp.offset_;
1806-
for (HighsInt iCol = 0; iCol < lp.num_col_; iCol++)
1823+
for (HighsInt iCol = 0; iCol < lp.num_col_; iCol++)
18071824
check_objective += solution[iCol] * lp.col_cost_[iCol];
18081825

1809-
double abs_dl_solution_objective = std::fabs(solution_objective - check_objective);
1810-
double rel_dl_solution_objective = abs_dl_solution_objective / (1.0 + std::fabs(mipsolver.solution_objective_));
1826+
double abs_dl_solution_objective =
1827+
std::fabs(solution_objective - check_objective);
1828+
double rel_dl_solution_objective =
1829+
abs_dl_solution_objective /
1830+
(1.0 + std::fabs(mipsolver.solution_objective_));
18111831
if (rel_dl_solution_objective > 1e-12) {
18121832
highsLogUser(log_options, HighsLogType::kError,
1813-
"HighsPrimalHeuristics::solveMipKnapsack() Relative optimal objective value mismatch of %g\n",
1814-
rel_dl_solution_objective);
1815-
mipsolver.modelstatus_ = HighsModelStatus::kSolveError;
1816-
return solveMipKnapsackReturn(HighsStatus::kError);
1833+
"HighsPrimalHeuristics::solveMipKnapsack() Relative optimal "
1834+
"objective value mismatch of %g\n",
1835+
rel_dl_solution_objective);
1836+
mipsolver.modelstatus_ = HighsModelStatus::kSolveError;
1837+
return solveMipKnapsackReturn(HighsStatus::kError);
18171838
}
18181839
mipsolver.solution_objective_ = solution_objective;
18191840

18201841
assert(mipsolver.modelstatus_ == HighsModelStatus::kOptimal);
18211842
return solveMipKnapsackReturn(HighsStatus::kOk);
18221843
}
1823-

0 commit comments

Comments
 (0)