Skip to content

Commit 10839f7

Browse files
committed
Knapsack solver looks correct; clean up some names
1 parent 74387dd commit 10839f7

File tree

4 files changed

+73
-43
lines changed

4 files changed

+73
-43
lines changed

highs/lp_data/HighsLp.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,14 +45,14 @@ bool HighsLp::isKnapsack(HighsInt& knapsack_rhs) const {
4545
// Must be one-sided constraint
4646
if (this->row_lower_[0] > -kHighsInf && this->row_upper_[0] < kHighsInf) return false;
4747
const bool upper = this->row_upper_[0] < kHighsInf;
48-
const HighsInt sign = upper ? 1 : -1;
48+
const HighsInt constraint_sign = upper ? 1 : -1;
4949
// Now check that all the (signed) coefficients are non-negative
5050
for (HighsInt iEl = 0; iEl < this->a_matrix_.numNz(); iEl++)
51-
if (sign * this->a_matrix_.value_[iEl] < 0) return false;
51+
if (constraint_sign * this->a_matrix_.value_[iEl] < 0) return false;
5252
// Problem is knapsack!
5353
//
5454
// Get the RHS: if it is negative, then the problem is infeasible
55-
knapsack_rhs = upper ? this->row_upper_[0] : sign*this->row_lower_[0];
55+
knapsack_rhs = upper ? this->row_upper_[0] : constraint_sign*this->row_lower_[0];
5656
assert(knapsack_rhs >= 0);
5757
return true;
5858
}

highs/mip/HighsMipSolver.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ void HighsMipSolver::run() {
9797
mipdata_->knapsack_data_.sum_rhs += knapsack_rhs;
9898
// Solve as a knapsack
9999
HighsStatus call_status = mipdata_->heuristics.solveKnapsack();
100-
printf("HighsMipSolver: Knapsack solver return status is %d\n", int(call_status));
100+
assert(call_status == HighsStatus::kOk);
101101
cleanupSolve();
102102
return;
103103
}

highs/mip/HighsPrimalHeuristics.cpp

Lines changed: 68 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1653,58 +1653,93 @@ double knapsackRecurrence(const std::vector<HighsInt>& weights,
16531653
const double rhs,
16541654
std::vector<std::vector<double>> &dp,
16551655
std::vector<std::vector<bool>> &use) {
1656-
if (num_col == 0 || rhs == 0)
1657-
return 0; // Base case
1656+
if (num_col == 0 || rhs == 0)
1657+
return 0; // Base case
16581658

1659-
if (dp[num_col][rhs] != -1) return dp[num_col][rhs]; // Check if result is already computed
1659+
if (dp[num_col][rhs] != -1) return dp[num_col][rhs]; // Check if result is already computed
16601660

1661-
// Exclude the item
1662-
double exclude = knapsackRecurrence(weights, values, num_col - 1, rhs, dp, use);
1661+
// Exclude the item
1662+
double exclude = knapsackRecurrence(weights, values, num_col-1, rhs, dp, use);
16631663

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);
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);
16681668

1669-
use[num_col][rhs] = include > exclude;
1670-
dp[num_col][rhs] = use[num_col][rhs] ? include : exclude; // Store the result
1669+
// Store whether the item is used with this RHS
1670+
use[num_col][rhs] = include > exclude;
1671+
// Store the result
1672+
dp[num_col][rhs] = use[num_col][rhs] ? include : exclude;
16711673

1672-
return dp[num_col][rhs];
1674+
return dp[num_col][rhs];
16731675

16741676
}
1677+
HighsStatus HighsPrimalHeuristics::solveKnapsackReturn(const HighsStatus& return_status) {
1678+
const HighsLp& lp = *(mipsolver.model_);
1679+
if (mipsolver.modelstatus_ == HighsModelStatus::kOptimal) {
1680+
// mipsolver.solution_objective_ is the objective value for the
1681+
// original problem - using the offset and ignoring the
1682+
// optimization sense. The mipdata_->lower/upper_bound values
1683+
// relate to the problem within the MIP solver. This is a
1684+
// minimization without offset
1685+
HighsInt sense = HighsInt(lp.sense_);
1686+
double mipsolver_objective = sense * mipsolver.solution_objective_ - lp.offset_;
1687+
mipsolver.bound_violation_ = 0;
1688+
mipsolver.integrality_violation_ = 0;
1689+
mipsolver.row_violation_ = 0;
1690+
mipsolver.mipdata_->lower_bound = mipsolver_objective;
1691+
mipsolver.mipdata_->upper_bound = mipsolver_objective;
1692+
mipsolver.gap_ = 0;
1693+
}
1694+
return return_status;
1695+
1696+
}
1697+
16751698
HighsStatus HighsPrimalHeuristics::solveKnapsack() {
1699+
HighsLp lp = *(mipsolver.model_);
1700+
// const HighsLp& lp = mipsolver.mipdata_->model_;
16761701
HighsInt knapsack_rhs_;
1677-
assert(mipsolver.orig_model_->isKnapsack(knapsack_rhs_));
1702+
assert(lp.isKnapsack(knapsack_rhs_));
16781703
const HighsInt knapsack_rhs = knapsack_rhs_;
16791704

1680-
HighsLp lp = *(mipsolver.orig_model_);
1681-
// const HighsLp& lp = mipsolver.mipdata_->orig_model_;
16821705
const bool upper = lp.row_upper_[0] < kHighsInf;
1683-
const HighsInt sign = upper ? 1 : -1;
1706+
const HighsInt constraint_sign = upper ? 1 : -1;
16841707
if (knapsack_rhs < 0) {
16851708
mipsolver.modelstatus_ = HighsModelStatus::kInfeasible;
1686-
return HighsStatus::kOk;
1709+
return solveKnapsackReturn(HighsStatus::kOk);
16871710
} else if (knapsack_rhs == 0) {
16881711
// Trivial knapsack with zero solution
16891712
mipsolver.solution_.assign(lp.num_col_, 0);
16901713
mipsolver.solution_objective_ = lp.offset_;
16911714
mipsolver.modelstatus_ = HighsModelStatus::kOptimal;
1692-
return HighsStatus::kOk;
1715+
return solveKnapsackReturn(HighsStatus::kOk);
16931716
}
1717+
// Set up the weightsfor the knapsack solver. They might not all be
1718+
// nonzero, so have to scatter into a zeroed vector
16941719
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]);
1720+
assert(lp.a_matrix_.format_ == MatrixFormat::kColwise);
1721+
for (HighsInt iCol = 0; iCol < lp.num_col_; iCol++) {
1722+
for (HighsInt iEl = lp.a_matrix_.start_[iCol]; iEl < lp.a_matrix_.start_[iCol+1]; iEl++)
1723+
weights[iCol] = HighsInt(constraint_sign * lp.a_matrix_.value_[iEl]);
16981724
}
16991725
HighsInt sense = HighsInt(lp.sense_);
1726+
// Set up the values for the knapsack solver. Since it solves a
1727+
// maximization problem, have to negate the costs if MIP is a
1728+
// minimization
17001729
std::vector<double> values;
17011730
for (HighsInt iCol = 0; iCol < lp.num_col_; iCol++)
1702-
values.push_back(sense*lp.col_cost_[iCol]);
1731+
values.push_back(-sense * lp.col_cost_[iCol]);
1732+
// Set up the DP array, indicating that no objectives are know
17031733
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));
1734+
// Set up the item use array, indicating that items are not used
1735+
std::vector<std::vector<bool>> use(lp.num_col_ + 1, std::vector<bool>(knapsack_rhs + 1, false));
1736+
// Solve the knapsack problem by DP
17051737
double knapsack_optimal_objective_value = knapsackRecurrence(weights, values, lp.num_col_, knapsack_rhs, dp, use);
17061738
// Deduce the solution
17071739
std::vector<HighsInt> knapsack_solution(lp.num_col_, 0);
1740+
// Variables are set to 1 if "used", and have to track the RHS of
1741+
// the subproblem after variables are assigned so that the correct
1742+
// entry of use is accessed
17081743
HighsInt knapsack_solution_rhs = knapsack_rhs;
17091744
for (HighsInt iCol = 0; iCol < lp.num_col_; iCol++) {
17101745
if (use[iCol][knapsack_solution_rhs]) {
@@ -1717,39 +1752,33 @@ HighsStatus HighsPrimalHeuristics::solveKnapsack() {
17171752

17181753
if (rel_row_violation > 1e-12) {
17191754
highsLogUser(mipsolver.options_mip_->log_options, HighsLogType::kError,
1720-
"HighsPrimalHeuristics::solveKnapsack() Relative capacity violation of %g\n", rel_row_violation);
1755+
"HighsPrimalHeuristics::solveKnapsack() (Abs; rel) capacity violation is (%g, %g)\n", row_violation, rel_row_violation);
17211756
mipsolver.modelstatus_ = HighsModelStatus::kSolveError;
1722-
return HighsStatus::kError;
1757+
return solveKnapsackReturn(HighsStatus::kError);
17231758
}
1724-
const double solution_objective = lp.offset_ + sense * knapsack_optimal_objective_value;
1759+
// Get the objective value corresponding to the original problem
1760+
const double solution_objective = lp.offset_ - sense * knapsack_optimal_objective_value;
17251761

1726-
// Compute the objective directly
1762+
// Compute the objective directly as a check
17271763
double check_objective = lp.offset_;
17281764
for (HighsInt iCol = 0; iCol < lp.num_col_; iCol++)
1729-
check_objective += knapsack_solution[iCol]*lp.col_cost_[iCol];
1765+
check_objective += knapsack_solution[iCol] * lp.col_cost_[iCol];
17301766

1731-
double abs_dl_solution_objective = std::fabs(mipsolver.solution_objective_ - check_objective);
1767+
double abs_dl_solution_objective = std::fabs(solution_objective - check_objective);
17321768
double rel_dl_solution_objective = abs_dl_solution_objective / (1.0 + std::fabs(mipsolver.solution_objective_));
17331769
if (rel_dl_solution_objective > 1e-12) {
17341770
highsLogUser(mipsolver.options_mip_->log_options, HighsLogType::kError,
17351771
"HighsPrimalHeuristics::solveKnapsack() Relative optimal objective value mismatch of %g\n", rel_dl_solution_objective);
17361772
mipsolver.modelstatus_ = HighsModelStatus::kSolveError;
1737-
return HighsStatus::kError;
1773+
return solveKnapsackReturn(HighsStatus::kError);
17381774
}
1739-
// Copy in the solution and compute the objective directly
1775+
// Copy in the solution
17401776
mipsolver.solution_.resize(lp.num_col_);
17411777
for (HighsInt iCol = 0; iCol < lp.num_col_; iCol++)
17421778
mipsolver.solution_[iCol] = knapsack_solution[iCol];
17431779
mipsolver.solution_objective_ = solution_objective;
17441780

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-
17521781
mipsolver.modelstatus_ = HighsModelStatus::kOptimal;
1753-
return HighsStatus::kOk;
1782+
return solveKnapsackReturn(HighsStatus::kOk);
17541783
}
17551784

highs/mip/HighsPrimalHeuristics.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ class HighsPrimalHeuristics {
7070

7171
void ziRound(const std::vector<double>& relaxationsol);
7272

73+
HighsStatus solveKnapsackReturn(const HighsStatus& return_status);
7374
HighsStatus solveKnapsack();
7475
};
7576

0 commit comments

Comments
 (0)