@@ -1650,31 +1650,72 @@ void HighsPrimalHeuristics::flushStatistics() {
16501650double knapsackRecurrence (const std::vector<HighsInt>& weight,
16511651 const std::vector<double >& value,
16521652 const HighsInt num_item,
1653- const double rhs ,
1653+ const double capacity ,
16541654 std::vector<std::vector<double >> &dp_result,
16551655 std::vector<std::vector<bool >> &use_item) {
1656- if (num_item == 0 || rhs == 0 )
1656+ if (num_item == 0 || capacity == 0 )
16571657 return 0 ; // Base case
16581658
1659- if (dp_result[num_item][rhs ] != -1 ) return dp_result[num_item][rhs ]; // Check if result is already computed
1659+ if (dp_result[num_item][capacity ] != -1 ) return dp_result[num_item][capacity ]; // Check if result is already computed
16601660
16611661 // Exclude the item
1662- double exclude = knapsackRecurrence (weight, value, num_item-1 , rhs , dp_result, use_item);
1662+ double exclude = knapsackRecurrence (weight, value, num_item-1 , capacity , dp_result, use_item);
16631663
16641664 // Include the item (if it fits in the knapsack)
16651665 double include = 0 ;
1666- if (weight[num_item-1 ] <= rhs )
1667- include = value[num_item-1 ] + knapsackRecurrence (weight, value, num_item-1 , rhs - weight[num_item-1 ], dp_result, use_item);
1666+ if (weight[num_item-1 ] <= capacity )
1667+ include = value[num_item-1 ] + knapsackRecurrence (weight, value, num_item-1 , capacity - weight[num_item-1 ], dp_result, use_item);
16681668
1669- // Store whether the item is used with this RHS
1670- use_item[num_item][rhs ] = include > exclude;
1669+ // Store whether the item is used with this capacity
1670+ use_item[num_item][capacity ] = include > exclude;
16711671 // Store the result
1672- dp_result[num_item][rhs ] = use_item[num_item][rhs ] ? include : exclude;
1672+ dp_result[num_item][capacity ] = use_item[num_item][capacity ] ? include : exclude;
16731673
1674- return dp_result[num_item][rhs ];
1674+ return dp_result[num_item][capacity ];
16751675
16761676}
1677- HighsStatus HighsPrimalHeuristics::solveKnapsackReturn (const HighsStatus& return_status) {
1677+
1678+ HighsModelStatus solveKnapsack (const HighsLogOptions& log_options,
1679+ const HighsInt num_item,
1680+ const std::vector<double >& value,
1681+ const std::vector<HighsInt>& weight,
1682+ const HighsInt capacity,
1683+ double & solution_objective,
1684+ std::vector<double >& solution) {
1685+ assert (capacity > 0 );
1686+
1687+ // Set up the DP result array, indicating that no optimal objective
1688+ // values are known
1689+ std::vector<std::vector<double >> dp_result (num_item + 1 , std::vector<double >(capacity + 1 , -1 ));
1690+ // Set up the item use array, indicating that items are not used
1691+ std::vector<std::vector<bool >> use_item (num_item + 1 , std::vector<bool >(capacity + 1 , false ));
1692+
1693+ solution_objective = knapsackRecurrence (weight, value, num_item, capacity, dp_result, use_item);
1694+
1695+ // Deduce the solution
1696+ std::vector<HighsInt> knapsack_solution (num_item, 0 );
1697+ // Variables are set to 1 if "used", and have to track the RHS of
1698+ // the subproblem after variables are assigned so that the correct
1699+ // entry of use is accessed
1700+ solution.resize (num_item);
1701+ HighsInt capacity_slack = capacity;
1702+ for (HighsInt iCol = 0 ; iCol < num_item; iCol++) {
1703+ if (use_item[iCol][capacity_slack]) {
1704+ solution[iCol] = 1.0 ;
1705+ capacity_slack -= weight[iCol];
1706+ }
1707+ }
1708+ const HighsInt capacity_violation = std::max (0 , -capacity_slack);
1709+ if (capacity_violation > 0 ) {
1710+ highsLogUser (log_options, HighsLogType::kError ,
1711+ " HighsPrimalHeuristics::solveKnapsack() Capacity violation is (%d)\n " ,
1712+ int (capacity_violation));
1713+ return HighsModelStatus::kSolveError ;
1714+ }
1715+ return HighsModelStatus::kOptimal ;
1716+ }
1717+
1718+ HighsStatus HighsPrimalHeuristics::solveMipKnapsackReturn (const HighsStatus& return_status) {
16781719 const HighsLp& lp = *(mipsolver.model_ );
16791720 if (mipsolver.modelstatus_ == HighsModelStatus::kOptimal ) {
16801721 // mipsolver.solution_objective_ is the objective value for the
@@ -1690,14 +1731,16 @@ HighsStatus HighsPrimalHeuristics::solveKnapsackReturn(const HighsStatus& return
16901731 mipsolver.mipdata_ ->lower_bound = mipsolver_objective;
16911732 mipsolver.mipdata_ ->upper_bound = mipsolver_objective;
16921733 mipsolver.gap_ = 0 ;
1734+ } else {
1735+ mipsolver.solution_ .clear ();
16931736 }
16941737 return return_status;
1695-
16961738}
16971739
1698- HighsStatus HighsPrimalHeuristics::solveKnapsack () {
1740+ HighsStatus HighsPrimalHeuristics::solveMipKnapsack () {
16991741 HighsLp lp = *(mipsolver.model_ );
17001742 // const HighsLp& lp = mipsolver.mipdata_->model_;
1743+ const HighsLogOptions& log_options = mipsolver.options_mip_ ->log_options ;
17011744 HighsInt capacity_;
17021745 assert (lp.isKnapsack (capacity_));
17031746 const HighsInt capacity = capacity_;
@@ -1706,13 +1749,13 @@ HighsStatus HighsPrimalHeuristics::solveKnapsack() {
17061749 const HighsInt constraint_sign = upper ? 1 : -1 ;
17071750 if (capacity < 0 ) {
17081751 mipsolver.modelstatus_ = HighsModelStatus::kInfeasible ;
1709- return solveKnapsackReturn (HighsStatus::kOk );
1752+ return solveMipKnapsackReturn (HighsStatus::kOk );
17101753 } else if (capacity == 0 ) {
17111754 // Trivial knapsack with zero solution
17121755 mipsolver.solution_ .assign (lp.num_col_ , 0 );
17131756 mipsolver.solution_objective_ = lp.offset_ ;
17141757 mipsolver.modelstatus_ = HighsModelStatus::kOptimal ;
1715- return solveKnapsackReturn (HighsStatus::kOk );
1758+ return solveMipKnapsackReturn (HighsStatus::kOk );
17161759 }
17171760 // Set up the weights for the knapsack solver. They might not all be
17181761 // nonzero, so have to scatter into a zeroed vector
@@ -1729,57 +1772,35 @@ HighsStatus HighsPrimalHeuristics::solveKnapsack() {
17291772 std::vector<double > value;
17301773 for (HighsInt iCol = 0 ; iCol < lp.num_col_ ; iCol++)
17311774 value.push_back (-sense * lp.col_cost_ [iCol]);
1732- // Set up the DP result array, indicating that no objectives are
1733- // know
1734- std::vector<std::vector<double >> dp_result (lp.num_col_ + 1 , std::vector<double >(capacity + 1 , -1 ));
1735- // Set up the item use array, indicating that items are not used
1736- std::vector<std::vector<bool >> use_item (lp.num_col_ + 1 , std::vector<bool >(capacity + 1 , false ));
17371775 // Solve the knapsack problem by DP
1738- double knapsack_optimal_objective_value = knapsackRecurrence (weight, value, lp.num_col_ , capacity, dp_result, use_item);
1739- // Deduce the solution
1740- std::vector<HighsInt> knapsack_solution (lp.num_col_ , 0 );
1741- // Variables are set to 1 if "used", and have to track the RHS of
1742- // the subproblem after variables are assigned so that the correct
1743- // entry of use is accessed
1744- HighsInt knapsack_solution_rhs = capacity;
1745- for (HighsInt iCol = 0 ; iCol < lp.num_col_ ; iCol++) {
1746- if (use_item[iCol][knapsack_solution_rhs]) {
1747- knapsack_solution[iCol] = 1 ;
1748- knapsack_solution_rhs -= weight[iCol];
1749- }
1750- }
1751- const double row_violation = std::max (0 , -knapsack_solution_rhs);
1752- const double rel_row_violation = row_violation / (1.0 * capacity);
1776+ double knapsack_optimal_objective_value;
1777+ std::vector<double >& solution = mipsolver.solution_ ;
1778+ mipsolver.modelstatus_ = solveKnapsack (log_options,
1779+ lp.num_col_ , value, weight, capacity,
1780+ knapsack_optimal_objective_value, solution);
1781+ if (mipsolver.modelstatus_ != HighsModelStatus::kOptimal )
1782+ return solveMipKnapsackReturn (HighsStatus::kError );
17531783
1754- if (rel_row_violation > 1e-12 ) {
1755- highsLogUser (mipsolver.options_mip_ ->log_options , HighsLogType::kError ,
1756- " HighsPrimalHeuristics::solveKnapsack() (Abs; rel) capacity violation is (%g, %g)\n " , row_violation, rel_row_violation);
1757- mipsolver.modelstatus_ = HighsModelStatus::kSolveError ;
1758- return solveKnapsackReturn (HighsStatus::kError );
1759- }
17601784 // Get the objective value corresponding to the original problem
17611785 const double solution_objective = lp.offset_ - sense * knapsack_optimal_objective_value;
17621786
17631787 // Compute the objective directly as a check
17641788 double check_objective = lp.offset_ ;
17651789 for (HighsInt iCol = 0 ; iCol < lp.num_col_ ; iCol++)
1766- check_objective += knapsack_solution [iCol] * lp.col_cost_ [iCol];
1790+ check_objective += solution [iCol] * lp.col_cost_ [iCol];
17671791
17681792 double abs_dl_solution_objective = std::fabs (solution_objective - check_objective);
17691793 double rel_dl_solution_objective = abs_dl_solution_objective / (1.0 + std::fabs (mipsolver.solution_objective_ ));
17701794 if (rel_dl_solution_objective > 1e-12 ) {
1771- highsLogUser (mipsolver.options_mip_ ->log_options , HighsLogType::kError ,
1772- " HighsPrimalHeuristics::solveKnapsack() Relative optimal objective value mismatch of %g\n " , rel_dl_solution_objective);
1795+ highsLogUser (log_options, HighsLogType::kError ,
1796+ " HighsPrimalHeuristics::solveMipKnapsack() Relative optimal objective value mismatch of %g\n " ,
1797+ rel_dl_solution_objective);
17731798 mipsolver.modelstatus_ = HighsModelStatus::kSolveError ;
1774- return solveKnapsackReturn (HighsStatus::kError );
1799+ return solveMipKnapsackReturn (HighsStatus::kError );
17751800 }
1776- // Copy in the solution
1777- mipsolver.solution_ .resize (lp.num_col_ );
1778- for (HighsInt iCol = 0 ; iCol < lp.num_col_ ; iCol++)
1779- mipsolver.solution_ [iCol] = knapsack_solution[iCol];
17801801 mipsolver.solution_objective_ = solution_objective;
17811802
1782- mipsolver.modelstatus_ = HighsModelStatus::kOptimal ;
1783- return solveKnapsackReturn (HighsStatus::kOk );
1803+ assert ( mipsolver.modelstatus_ == HighsModelStatus::kOptimal ) ;
1804+ return solveMipKnapsackReturn (HighsStatus::kOk );
17841805}
17851806
0 commit comments