Skip to content

Commit 74387dd

Browse files
committed
Integrated knapsack solver: now debug its use
1 parent 7da8617 commit 74387dd

File tree

5 files changed

+117
-3
lines changed

5 files changed

+117
-3
lines changed

highs/lp_data/HighsLp.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ bool HighsLp::isBip() const {
3737
return true;
3838
}
3939

40-
bool HighsLp::isKnapsack(double& knapsack_rhs) const {
40+
bool HighsLp::isKnapsack(HighsInt& knapsack_rhs) const {
4141
// Has to kave one constraint
4242
if (this->num_row_ != 1) return false;
4343
// Has to be a binary integer programming problem

highs/lp_data/HighsLp.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ class HighsLp {
6666
bool equalScaling(const HighsLp& lp) const;
6767
bool isMip() const;
6868
bool isBip() const;
69-
bool isKnapsack(double& knapsack_rhs) const;
69+
bool isKnapsack(HighsInt& knapsack_rhs) const;
7070
bool hasSemiVariables() const;
7171
bool hasInfiniteCost(const double infinite_cost) const;
7272
bool hasMods() const;

highs/mip/HighsMipSolver.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,11 +90,16 @@ void HighsMipSolver::run() {
9090

9191
// Determine whether this is a knapsack problem and, if so, at least
9292
// update the data on knapsack sub-MIPs
93-
double knapsack_rhs = 0;
93+
HighsInt knapsack_rhs = 0;
9494
if (orig_model_->isKnapsack(knapsack_rhs)) {
9595
mipdata_->knapsack_data_.num_problem++;
9696
mipdata_->knapsack_data_.sum_variables += orig_model_->num_col_;
9797
mipdata_->knapsack_data_.sum_rhs += knapsack_rhs;
98+
// Solve as a knapsack
99+
HighsStatus call_status = mipdata_->heuristics.solveKnapsack();
100+
printf("HighsMipSolver: Knapsack solver return status is %d\n", int(call_status));
101+
cleanupSolve();
102+
return;
98103
}
99104

100105
analysis_.mipTimerStart(kMipClockRunPresolve);

highs/mip/HighsPrimalHeuristics.cpp

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1646,3 +1646,110 @@ void HighsPrimalHeuristics::flushStatistics() {
16461646
mipsolver.mipdata_->total_lp_iterations += lp_iterations;
16471647
lp_iterations = 0;
16481648
}
1649+
1650+
double knapsackRecurrence(const std::vector<HighsInt>& weights,
1651+
const std::vector<double>& values,
1652+
const HighsInt num_col,
1653+
const double rhs,
1654+
std::vector<std::vector<double>> &dp,
1655+
std::vector<std::vector<bool>> &use) {
1656+
if (num_col == 0 || rhs == 0)
1657+
return 0; // Base case
1658+
1659+
if (dp[num_col][rhs] != -1) return dp[num_col][rhs]; // Check if result is already computed
1660+
1661+
// Exclude the item
1662+
double exclude = knapsackRecurrence(weights, values, num_col - 1, rhs, dp, use);
1663+
1664+
// Include the item (if it fits in the knapsack)
1665+
double include = 0;
1666+
if (weights[num_col - 1] <= rhs)
1667+
include = values[num_col - 1] + knapsackRecurrence(weights, values, num_col - 1, rhs - weights[num_col - 1], dp, use);
1668+
1669+
use[num_col][rhs] = include > exclude;
1670+
dp[num_col][rhs] = use[num_col][rhs] ? include : exclude; // Store the result
1671+
1672+
return dp[num_col][rhs];
1673+
1674+
}
1675+
HighsStatus HighsPrimalHeuristics::solveKnapsack() {
1676+
HighsInt knapsack_rhs_;
1677+
assert(mipsolver.orig_model_->isKnapsack(knapsack_rhs_));
1678+
const HighsInt knapsack_rhs = knapsack_rhs_;
1679+
1680+
HighsLp lp = *(mipsolver.orig_model_);
1681+
// const HighsLp& lp = mipsolver.mipdata_->orig_model_;
1682+
const bool upper = lp.row_upper_[0] < kHighsInf;
1683+
const HighsInt sign = upper ? 1 : -1;
1684+
if (knapsack_rhs < 0) {
1685+
mipsolver.modelstatus_ = HighsModelStatus::kInfeasible;
1686+
return HighsStatus::kOk;
1687+
} else if (knapsack_rhs == 0) {
1688+
// Trivial knapsack with zero solution
1689+
mipsolver.solution_.assign(lp.num_col_, 0);
1690+
mipsolver.solution_objective_ = lp.offset_;
1691+
mipsolver.modelstatus_ = HighsModelStatus::kOptimal;
1692+
return HighsStatus::kOk;
1693+
}
1694+
std::vector<HighsInt> weights(lp.num_col_, 0);
1695+
for (HighsInt iEl = 0; iEl < lp.a_matrix_.numNz(); iEl++) {
1696+
HighsInt iCol = lp.a_matrix_.index_[iEl];
1697+
weights[iCol] = HighsInt(sign * lp.a_matrix_.value_[iEl]);
1698+
}
1699+
HighsInt sense = HighsInt(lp.sense_);
1700+
std::vector<double> values;
1701+
for (HighsInt iCol = 0; iCol < lp.num_col_; iCol++)
1702+
values.push_back(sense*lp.col_cost_[iCol]);
1703+
std::vector<std::vector<double>> dp(lp.num_col_ + 1, std::vector<double>(knapsack_rhs + 1, -1));
1704+
std::vector<std::vector<bool>> use(lp.num_col_ + 1, std::vector<bool>(knapsack_rhs + 1, -1));
1705+
double knapsack_optimal_objective_value = knapsackRecurrence(weights, values, lp.num_col_, knapsack_rhs, dp, use);
1706+
// Deduce the solution
1707+
std::vector<HighsInt> knapsack_solution(lp.num_col_, 0);
1708+
HighsInt knapsack_solution_rhs = knapsack_rhs;
1709+
for (HighsInt iCol = 0; iCol < lp.num_col_; iCol++) {
1710+
if (use[iCol][knapsack_solution_rhs]) {
1711+
knapsack_solution[iCol] = 1;
1712+
knapsack_solution_rhs -= weights[iCol];
1713+
}
1714+
}
1715+
const double row_violation = std::max(0, -knapsack_solution_rhs);
1716+
const double rel_row_violation = row_violation / (1.0 * knapsack_rhs);
1717+
1718+
if (rel_row_violation > 1e-12) {
1719+
highsLogUser(mipsolver.options_mip_->log_options, HighsLogType::kError,
1720+
"HighsPrimalHeuristics::solveKnapsack() Relative capacity violation of %g\n", rel_row_violation);
1721+
mipsolver.modelstatus_ = HighsModelStatus::kSolveError;
1722+
return HighsStatus::kError;
1723+
}
1724+
const double solution_objective = lp.offset_ + sense * knapsack_optimal_objective_value;
1725+
1726+
// Compute the objective directly
1727+
double check_objective = lp.offset_;
1728+
for (HighsInt iCol = 0; iCol < lp.num_col_; iCol++)
1729+
check_objective += knapsack_solution[iCol]*lp.col_cost_[iCol];
1730+
1731+
double abs_dl_solution_objective = std::fabs(mipsolver.solution_objective_ - check_objective);
1732+
double rel_dl_solution_objective = abs_dl_solution_objective / (1.0 + std::fabs(mipsolver.solution_objective_));
1733+
if (rel_dl_solution_objective > 1e-12) {
1734+
highsLogUser(mipsolver.options_mip_->log_options, HighsLogType::kError,
1735+
"HighsPrimalHeuristics::solveKnapsack() Relative optimal objective value mismatch of %g\n", rel_dl_solution_objective);
1736+
mipsolver.modelstatus_ = HighsModelStatus::kSolveError;
1737+
return HighsStatus::kError;
1738+
}
1739+
// Copy in the solution and compute the objective directly
1740+
mipsolver.solution_.resize(lp.num_col_);
1741+
for (HighsInt iCol = 0; iCol < lp.num_col_; iCol++)
1742+
mipsolver.solution_[iCol] = knapsack_solution[iCol];
1743+
mipsolver.solution_objective_ = solution_objective;
1744+
1745+
mipsolver.bound_violation_ = 0;
1746+
mipsolver.integrality_violation_ = 0;
1747+
mipsolver.row_violation_ = 0;
1748+
mipsolver.dual_bound_ = mipsolver.solution_objective_;
1749+
mipsolver.primal_bound_ = mipsolver.solution_objective_;
1750+
mipsolver.gap_ = 0;
1751+
1752+
mipsolver.modelstatus_ = HighsModelStatus::kOptimal;
1753+
return HighsStatus::kOk;
1754+
}
1755+

highs/mip/HighsPrimalHeuristics.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ class HighsPrimalHeuristics {
6969
void shifting(const std::vector<double>& relaxationsol);
7070

7171
void ziRound(const std::vector<double>& relaxationsol);
72+
73+
HighsStatus solveKnapsack();
7274
};
7375

7476
#endif

0 commit comments

Comments
 (0)