Google OR-Tools v9.15
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
knapsack_solver.h
Go to the documentation of this file.
1// Copyright 2010-2025 Google LLC
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
14#ifndef ORTOOLS_ALGORITHMS_KNAPSACK_SOLVER_H_
15#define ORTOOLS_ALGORITHMS_KNAPSACK_SOLVER_H_
16
17#include <cstdint>
18#include <memory>
19#include <string>
20#include <vector>
21
22#include "absl/strings/string_view.h"
24
25namespace operations_research {
26
28
104 public:
184
185 explicit KnapsackSolver(const std::string& solver_name);
186 KnapsackSolver(SolverType solver_type, const std::string& solver_name);
187
188#ifndef SWIG
189 // This type is neither copyable nor movable.
192#endif
193
195
197 void Init(const std::vector<int64_t>& profits,
198 const std::vector<std::vector<int64_t> >& weights,
199 const std::vector<int64_t>& capacities);
200
202 int64_t Solve();
203
205 bool BestSolutionContains(int item_id) const;
207 bool IsSolutionOptimal() const { return is_solution_optimal_; }
208 std::string GetName() const;
209
210 bool use_reduction() const { return use_reduction_; }
211 void set_use_reduction(bool use_reduction) { use_reduction_ = use_reduction; }
212
216 void set_time_limit(double time_limit_seconds) {
217 time_limit_seconds_ = time_limit_seconds;
218 time_limit_ = std::make_unique<TimeLimit>(time_limit_seconds_);
219 }
220
221 private:
224 int ReduceCapacities(int num_items,
225 const std::vector<std::vector<int64_t> >& weights,
226 const std::vector<int64_t>& capacities,
227 std::vector<std::vector<int64_t> >* reduced_weights,
228 std::vector<int64_t>* reduced_capacities);
229 int ReduceProblem(int num_items);
230 void ComputeAdditionalProfit(const std::vector<int64_t>& profits);
231 void InitReducedProblem(const std::vector<int64_t>& profits,
232 const std::vector<std::vector<int64_t> >& weights,
233 const std::vector<int64_t>& capacities);
234
235 std::unique_ptr<BaseKnapsackSolver> solver_;
236 std::vector<bool> known_value_;
237 std::vector<bool> best_solution_;
238 bool is_solution_optimal_ = false;
239 std::vector<int> mapping_reduced_item_id_;
240 bool is_problem_solved_;
241 int64_t additional_profit_;
242 bool use_reduction_;
243 double time_limit_seconds_;
244 std::unique_ptr<TimeLimit> time_limit_;
245};
246
247#if !defined(SWIG)
248// The following code defines needed classes for the KnapsackGenericSolver
249// class which is the entry point to extend knapsack with new constraints such
250// as conflicts between items.
251//
252// Constraints are enforced using KnapsackPropagator objects, in the current
253// code there is one propagator per dimension (KnapsackCapacityPropagator).
254// One of those propagators, named primary propagator, is used to guide the
255// search, i.e. decides which item should be assigned next.
256// Roughly speaking the search algorithm is:
257// - While not optimal
258// - Select next search node to expand
259// - Select next item_i to assign (using primary propagator)
260// - Generate a new search node where item_i is in the knapsack
261// - Check validity of this new partial solution (using propagators)
262// - If valid, add this new search node to the search
263// - Generate a new search node where item_i is not in the knapsack
264// - Check validity of this new partial solution (using propagators)
265// - If valid, add this new search node to the search
266//
267// TODO(user): Add a new propagator class for conflict constraint.
268// TODO(user): Add a new propagator class used as a guide when the problem has
269// several dimensions.
270
271// ----- KnapsackAssignment -----
275 KnapsackAssignment(int _item_id, bool _is_in)
276 : item_id(_item_id), is_in(_is_in) {}
278 bool is_in;
279};
280
281// ----- KnapsackItem -----
294 KnapsackItem(int _id, int64_t _weight, int64_t _profit)
295 : id(_id), weight(_weight), profit(_profit) {}
296 double GetEfficiency(int64_t profit_max) const {
297 return (weight > 0)
298 ? static_cast<double>(profit) / static_cast<double>(weight)
299 : static_cast<double>(profit_max);
300 }
301
304 const int id;
305 const int64_t weight;
306 const int64_t profit;
307};
309
310// ----- KnapsackSearchNode -----
319 public:
322
323#ifndef SWIG
324 // This type is neither copyable nor movable.
327#endif
328
329 int depth() const { return depth_; }
330 const KnapsackSearchNode* parent() const { return parent_; }
331 const KnapsackAssignment& assignment() const { return assignment_; }
332
333 int64_t current_profit() const { return current_profit_; }
334 void set_current_profit(int64_t profit) { current_profit_ = profit; }
335
336 int64_t profit_upper_bound() const { return profit_upper_bound_; }
337 void set_profit_upper_bound(int64_t profit) { profit_upper_bound_ = profit; }
338
339 int next_item_id() const { return next_item_id_; }
340 void set_next_item_id(int id) { next_item_id_ = id; }
341
342 private:
345 int depth_;
346 const KnapsackSearchNode* const parent_;
347 KnapsackAssignment assignment_;
348
353 int64_t current_profit_;
354 int64_t profit_upper_bound_;
355
358 int next_item_id_;
359};
360
361// ----- KnapsackSearchPath -----
377class KnapsackSearchPath {
378 public:
380 const KnapsackSearchNode& to);
381
382#ifndef SWIG
383 // This type is neither copyable nor movable.
384 KnapsackSearchPath(const KnapsackSearchPath&) = delete;
386#endif
388 void Init();
389 const KnapsackSearchNode& from() const { return from_; }
390 const KnapsackSearchNode& via() const { return *via_; }
391 const KnapsackSearchNode& to() const { return to_; }
393 int depth) const;
394
395 private:
396 const KnapsackSearchNode& from_;
397 const KnapsackSearchNode* via_; // Computed in 'Init'.
398 const KnapsackSearchNode& to_;
399};
400
401// ----- KnapsackState -----
402
403class KnapsackState {
404 public:
406
407#ifndef SWIG
408 // This type is neither copyable nor movable.
409 KnapsackState(const KnapsackState&) = delete;
410 KnapsackState& operator=(const KnapsackState&) = delete;
411#endif
415 void Init(int number_of_items);
419 bool UpdateState(bool revert, const KnapsackAssignment& assignment);
420
421 int GetNumberOfItems() const { return is_bound_.size(); }
422 bool is_bound(int id) const { return is_bound_.at(id); }
423 bool is_in(int id) const { return is_in_.at(id); }
425 private:
430 std::vector<bool> is_bound_;
431 std::vector<bool> is_in_;
432};
433
434// ----- KnapsackPropagator -----
435
441class KnapsackPropagator {
442 public:
444
445#ifndef SWIG
446 // This type is neither copyable nor movable.
447 KnapsackPropagator(const KnapsackPropagator&) = delete;
449#endif
451 virtual ~KnapsackPropagator();
452
454 void Init(const std::vector<int64_t>& profits,
455 const std::vector<int64_t>& weights);
456
459 bool Update(bool revert, const KnapsackAssignment& assignment);
462 virtual void ComputeProfitBounds() = 0;
465 virtual int GetNextItemId() const = 0;
466
467 int64_t current_profit() const { return current_profit_; }
468 int64_t profit_lower_bound() const { return profit_lower_bound_; }
469 int64_t profit_upper_bound() const { return profit_upper_bound_; }
477 void CopyCurrentStateToSolution(bool has_one_propagator,
478 std::vector<bool>* solution) const;
479
480 protected:
483 virtual void InitPropagator() = 0;
484
487 virtual bool UpdatePropagator(bool revert,
488 const KnapsackAssignment& assignment) = 0;
496 std::vector<bool>* solution) const = 0;
498 const KnapsackState& state() const { return state_; }
499 const std::vector<KnapsackItemPtr>& items() const { return items_; }
501 void set_profit_lower_bound(int64_t profit) { profit_lower_bound_ = profit; }
502 void set_profit_upper_bound(int64_t profit) { profit_upper_bound_ = profit; }
504 private:
505 std::vector<KnapsackItemPtr> items_;
506 int64_t current_profit_;
507 int64_t profit_lower_bound_;
508 int64_t profit_upper_bound_;
509 const KnapsackState& state_;
510};
511
512// ----- KnapsackCapacityPropagator -----
513
531 public:
533
534#ifndef SWIG
535 // This type is neither copyable nor movable.
538 delete;
539#endif
540
542 void ComputeProfitBounds() override;
543 int GetNextItemId() const override { return break_item_id_; }
544
545 protected:
548 void InitPropagator() override;
551 bool UpdatePropagator(bool revert,
552 const KnapsackAssignment& assignment) override;
554 std::vector<bool>* solution) const override;
555
556 private:
565 int64_t GetAdditionalProfit(int64_t remaining_capacity,
566 int break_item_id) const;
567
568 const int64_t capacity_;
569 int64_t consumed_capacity_;
570 int break_item_id_;
571 std::vector<KnapsackItemPtr> sorted_items_;
572 int64_t profit_max_;
573};
574
575// ----- BaseKnapsackSolver -----
576
577class BaseKnapsackSolver {
578 public:
579 explicit BaseKnapsackSolver(absl::string_view solver_name)
580 : solver_name_(solver_name) {}
581 virtual ~BaseKnapsackSolver() = default;
582
583
584 virtual void Init(const std::vector<int64_t>& profits,
585 const std::vector<std::vector<int64_t> >& weights,
586 const std::vector<int64_t>& capacities) = 0;
587
588 // Gets the lower and upper bound when the item is in or out of the knapsack.
589 // To ensure objects are correctly initialized, this method should not be
590 // called before ::Init.
591 virtual void GetLowerAndUpperBoundWhenItem(int item_id, bool is_item_in,
592 int64_t* lower_bound,
593 int64_t* upper_bound);
594
596 virtual int64_t Solve(TimeLimit* time_limit, double time_limit_in_seconds,
597 bool* is_solution_optimal) = 0;
600 virtual bool best_solution(int item_id) const = 0;
601
602 virtual std::string GetName() const { return solver_name_; }
603
604 private:
605 const std::string solver_name_;
606};
607
608// ----- KnapsackGenericSolver -----
609
618 public:
619 explicit KnapsackGenericSolver(const std::string& solver_name);
620
621#ifndef SWIG
622 // This type is neither copyable nor movable.
625#endif
627 ~KnapsackGenericSolver() override;
628
630 void Init(const std::vector<int64_t>& profits,
631 const std::vector<std::vector<int64_t> >& weights,
632 const std::vector<int64_t>& capacities) override;
633 int GetNumberOfItems() const { return state_.GetNumberOfItems(); }
634 void GetLowerAndUpperBoundWhenItem(int item_id, bool is_item_in,
635 int64_t* lower_bound,
636 int64_t* upper_bound) override;
637
641 void set_primary_propagator_id(int primary_propagator_id) {
642 primary_propagator_id_ = primary_propagator_id;
644
646 int64_t Solve(TimeLimit* time_limit, double time_limit_in_seconds,
647 bool* is_solution_optimal) override;
649 bool best_solution(int item_id) const override {
650 return best_solution_.at(item_id);
652
653 private:
654
655 void Clear();
656
660 bool UpdatePropagators(const KnapsackSearchPath& path);
664 bool IncrementalUpdate(bool revert, const KnapsackAssignment& assignment);
666 void UpdateBestSolution();
667
670 bool MakeNewNode(const KnapsackSearchNode& node, bool is_in);
671
673 int64_t GetAggregatedProfitUpperBound() const;
674 bool HasOnePropagator() const { return propagators_.size() == 1; }
675 int64_t GetCurrentProfit() const {
676 return propagators_.at(primary_propagator_id_)->current_profit();
677 }
678 int64_t GetNextItemId() const {
679 return propagators_.at(primary_propagator_id_)->GetNextItemId();
680 }
681
682 std::vector<KnapsackPropagator*> propagators_;
683 int primary_propagator_id_;
684 std::vector<KnapsackSearchNode*> search_nodes_;
685 KnapsackState state_;
686 int64_t best_solution_profit_;
687 std::vector<bool> best_solution_;
688};
689#endif // SWIG
690} // namespace operations_research
691
692#endif // ORTOOLS_ALGORITHMS_KNAPSACK_SOLVER_H_
This is the base class for knapsack solvers.
virtual bool best_solution(int item_id) const =0
Returns true if the item 'item_id' is packed in the optimal knapsack.
virtual int64_t Solve(TimeLimit *time_limit, double time_limit_in_seconds, bool *is_solution_optimal)=0
Solves the problem and returns the profit of the optimal solution.
virtual void Init(const std::vector< int64_t > &profits, const std::vector< std::vector< int64_t > > &weights, const std::vector< int64_t > &capacities)=0
Initializes the solver and enters the problem to be solved.
BaseKnapsackSolver(absl::string_view solver_name)
virtual void GetLowerAndUpperBoundWhenItem(int item_id, bool is_item_in, int64_t *lower_bound, int64_t *upper_bound)
virtual std::string GetName() const
KnapsackCapacityPropagator is a KnapsackPropagator used to enforce a capacity constraint.
void CopyCurrentStateToSolutionPropagator(std::vector< bool > *solution) const override
KnapsackCapacityPropagator & operator=(const KnapsackCapacityPropagator &)=delete
KnapsackCapacityPropagator(const KnapsackState &state, int64_t capacity)
bool UpdatePropagator(bool revert, const KnapsackAssignment &assignment) override
KnapsackGenericSolver is the multi-dimensional knapsack solver class.
void GetLowerAndUpperBoundWhenItem(int item_id, bool is_item_in, int64_t *lower_bound, int64_t *upper_bound) override
void set_primary_propagator_id(int primary_propagator_id)
bool best_solution(int item_id) const override
Returns true if the item 'item_id' is packed in the optimal knapsack.
void Init(const std::vector< int64_t > &profits, const std::vector< std::vector< int64_t > > &weights, const std::vector< int64_t > &capacities) override
Initializes the solver and enters the problem to be solved.
KnapsackGenericSolver & operator=(const KnapsackGenericSolver &)=delete
KnapsackGenericSolver(const std::string &solver_name)
int64_t Solve(TimeLimit *time_limit, double time_limit_in_seconds, bool *is_solution_optimal) override
Solves the problem and returns the profit of the optimal solution.
KnapsackPropagator is the base class for modeling and propagating a constraint given an assignment.
KnapsackPropagator & operator=(const KnapsackPropagator &)=delete
virtual int GetNextItemId() const =0
void CopyCurrentStateToSolution(bool has_one_propagator, std::vector< bool > *solution) const
bool Update(bool revert, const KnapsackAssignment &assignment)
void Init(const std::vector< int64_t > &profits, const std::vector< int64_t > &weights)
Initializes data structure and then calls InitPropagator.
const KnapsackState & state() const
KnapsackPropagator(const KnapsackState &state)
virtual void CopyCurrentStateToSolutionPropagator(std::vector< bool > *solution) const =0
virtual bool UpdatePropagator(bool revert, const KnapsackAssignment &assignment)=0
const std::vector< KnapsackItemPtr > & items() const
KnapsackSearchNode(const KnapsackSearchNode *parent, const KnapsackAssignment &assignment)
const KnapsackAssignment & assignment() const
KnapsackSearchNode & operator=(const KnapsackSearchNode &)=delete
const KnapsackSearchNode * parent() const
KnapsackSearchNode(const KnapsackSearchNode &)=delete
const KnapsackSearchNode & via() const
KnapsackSearchPath(const KnapsackSearchNode &from, const KnapsackSearchNode &to)
const KnapsackSearchNode & to() const
const KnapsackSearchNode & from() const
const KnapsackSearchNode * MoveUpToDepth(const KnapsackSearchNode &node, int depth) const
KnapsackSearchPath & operator=(const KnapsackSearchPath &)=delete
void set_time_limit(double time_limit_seconds)
Time limit in seconds.
KnapsackSolver(const KnapsackSolver &)=delete
bool BestSolutionContains(int item_id) const
Returns true if the item 'item_id' is packed in the optimal knapsack.
void Init(const std::vector< int64_t > &profits, const std::vector< std::vector< int64_t > > &weights, const std::vector< int64_t > &capacities)
Initializes the solver and enters the problem to be solved.
KnapsackSolver(const std::string &solver_name)
SolverType
Enum controlling which underlying algorithm is used.
bool IsSolutionOptimal() const
Returns true if the solution was proven optimal.
KnapsackSolver & operator=(const KnapsackSolver &)=delete
void set_use_reduction(bool use_reduction)
int64_t Solve()
Solves the problem and returns the profit of the optimal solution.
KnapsackState represents a partial solution to the knapsack problem.
bool UpdateState(bool revert, const KnapsackAssignment &assignment)
KnapsackState & operator=(const KnapsackState &)=delete
OR-Tools root namespace.
Select next search node to expand Select next item_i to add this new search node to the search Generate a new search node where item_i is not in the knapsack Check validity of this new partial solution(using propagators) - If valid
KnapsackItem * KnapsackItemPtr
KnapsackAssignment(int _item_id, bool _is_in)
KnapsackItem(int _id, int64_t _weight, int64_t _profit)
double GetEfficiency(int64_t profit_max) const