├── .gitattributes ├── .github └── workflows │ └── build.yml ├── CMakeLists.txt ├── LICENSE ├── README.md ├── extern └── CMakeLists.txt ├── include └── knapsacksolver │ ├── knapsack │ ├── algorithm_formatter.hpp │ ├── algorithms │ │ ├── dynamic_programming_bellman.hpp │ │ ├── dynamic_programming_primal_dual.hpp │ │ ├── greedy.hpp │ │ ├── surrogate_relaxation.hpp │ │ └── upper_bound_dantzig.hpp │ ├── generator.hpp │ ├── instance.hpp │ ├── instance_builder.hpp │ ├── solution.hpp │ ├── sort.hpp │ ├── tests.hpp │ └── upper_bound.hpp │ ├── multiple_choice_subset_sum │ ├── algorithm_formatter.hpp │ ├── algorithms │ │ └── dynamic_programming_bellman.hpp │ ├── instance.hpp │ ├── instance_builder.hpp │ └── solution.hpp │ └── subset_sum │ ├── algorithm_formatter.hpp │ ├── algorithms │ ├── dynamic_programming_balancing.hpp │ ├── dynamic_programming_bellman.hpp │ └── dynamic_programming_primal_dual.hpp │ ├── generator.hpp │ ├── instance.hpp │ ├── instance_builder.hpp │ ├── solution.hpp │ └── tests.hpp ├── knapsack.png ├── python └── knapsacksolver.cpp ├── scripts ├── download_data.py └── run_tests.py ├── src ├── CMakeLists.txt ├── knapsack │ ├── CMakeLists.txt │ ├── algorithm_formatter.cpp │ ├── algorithms │ │ ├── CMakeLists.txt │ │ ├── dynamic_programming_bellman.cpp │ │ ├── dynamic_programming_primal_dual.cpp │ │ ├── greedy.cpp │ │ ├── greedy_test.cpp │ │ ├── surrogate_relaxation.cpp │ │ └── upper_bound_dantzig.cpp │ ├── generator.cpp │ ├── generator_main.cpp │ ├── instance.cpp │ ├── instance_builder.cpp │ ├── main.cpp │ ├── solution.cpp │ ├── sort.cpp │ ├── tests.cpp │ └── upper_bound.cpp ├── multiple_choice_subset_sum │ ├── CMakeLists.txt │ ├── algorithm_formatter.cpp │ ├── algorithms │ │ ├── CMakeLists.txt │ │ └── dynamic_programming_bellman.cpp │ ├── instance.cpp │ ├── instance_builder.cpp │ ├── main.cpp │ └── solution.cpp └── subset_sum │ ├── CMakeLists.txt │ ├── algorithm_formatter.cpp │ ├── algorithms │ ├── CMakeLists.txt │ ├── dynamic_programming_balancing.cpp │ ├── dynamic_programming_bellman.cpp │ └── dynamic_programming_primal_dual.cpp │ ├── generator.cpp │ ├── generator_main.cpp │ ├── instance.cpp │ ├── instance_builder.cpp │ ├── main.cpp │ ├── solution.cpp │ └── tests.cpp └── test ├── CMakeLists.txt ├── knapsack ├── CMakeLists.txt └── algorithms │ ├── CMakeLists.txt │ ├── dynamic_programming_bellman_test.cpp │ └── dynamic_programming_primal_dual_test.cpp ├── multiple_choice_subset_sum ├── CMakeLists.txt └── algorithms │ └── CMakeLists.txt └── subset_sum ├── CMakeLists.txt └── algorithms ├── CMakeLists.txt └── dynamic_programming_bellman_test.cpp /.gitattributes: -------------------------------------------------------------------------------- 1 | data/* linguist-vendored 2 | bench/* linguist-vendored 3 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: [push] 4 | 5 | jobs: 6 | 7 | build: 8 | 9 | runs-on: ${{ matrix.os }} 10 | 11 | strategy: 12 | fail-fast: false 13 | matrix: 14 | os: [ubuntu-latest, windows-latest, macos-latest] 15 | python-version: ["3.8"] 16 | 17 | env: 18 | KNAPSACK_DATA: ${{ github.workspace }}/data/knapsack 19 | SUBSET_SUM_DATA: ${{ github.workspace }}/data/subset_sum 20 | MULITPLE_CHOICE_SUBSET_SUM_DATA: ${{ github.workspace }}/data/multiple_choice_subset_sum 21 | 22 | steps: 23 | - name: Checkout code 24 | uses: actions/checkout@v4 25 | - name: Set up Python ${{ matrix.python-version }} 26 | uses: actions/setup-python@v4 27 | with: 28 | python-version: ${{ matrix.python-version }} 29 | - name: Download data 30 | run: | 31 | python3 -m pip install gdown 32 | python3 -u scripts/download_data.py 33 | - name: Build 34 | run: | 35 | cmake -S . -B build -DCMAKE_BUILD_TYPE=Release 36 | cmake --build build --config Release --parallel 37 | cmake --install build --config Release --prefix install 38 | - name: Run unit tests 39 | working-directory: build/test 40 | run: ctest --parallel 41 | - name: Run tests 42 | run: python3 -u scripts/run_tests.py test_results 43 | - name: Checkout main branch 44 | run: | 45 | git remote set-branches origin '*' 46 | git fetch --depth 1 47 | git checkout master 48 | - name: Build 49 | run: | 50 | cmake -S . -B build -DCMAKE_BUILD_TYPE=Release 51 | cmake --build build --config Release --parallel 52 | cmake --install build --config Release --prefix install 53 | - name: Run tests 54 | run: python3 -u scripts/run_tests.py test_results_ref 55 | - name: Process tests 56 | run: python3 -u ./build/_deps/optimizationtools-src/scripts/process_tests.py --ref test_results_ref --new test_results 57 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15.0) 2 | 3 | project(KnapsackSolver LANGUAGES CXX) 4 | 5 | # Avoid FetchContent warning. 6 | cmake_policy(SET CMP0135 NEW) 7 | 8 | # Require C++11. 9 | set(CMAKE_CXX_STANDARD 11) 10 | 11 | # Enable output of compile commands during generation. 12 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON) 13 | 14 | # Add sub-directories. 15 | add_subdirectory(extern) 16 | add_subdirectory(src) 17 | add_subdirectory(test) 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Florian Fontan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /extern/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Enable FetchContent. 2 | include(FetchContent) 3 | 4 | # Fetch boost. 5 | set(BOOST_INCLUDE_LIBRARIES thread filesystem system program_options) 6 | set(BOOST_ENABLE_CMAKE ON) 7 | FetchContent_Declare( 8 | Boost 9 | URL https://github.com/boostorg/boost/releases/download/boost-1.84.0/boost-1.84.0.tar.xz 10 | EXCLUDE_FROM_ALL) 11 | FetchContent_MakeAvailable(Boost) 12 | 13 | # Fetch fontanf/optimizationtools. 14 | FetchContent_Declare( 15 | optimizationtools 16 | GIT_REPOSITORY https://github.com/fontanf/optimizationtools.git 17 | GIT_TAG 4037a1a03f97ea0c388baa4f3f74c3ba55baec08 18 | #SOURCE_DIR "${PROJECT_SOURCE_DIR}/../optimizationtools/" 19 | EXCLUDE_FROM_ALL) 20 | FetchContent_MakeAvailable(optimizationtools) 21 | -------------------------------------------------------------------------------- /include/knapsacksolver/knapsack/algorithm_formatter.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "knapsacksolver/knapsack/solution.hpp" 4 | 5 | namespace knapsacksolver 6 | { 7 | namespace knapsack 8 | { 9 | 10 | class AlgorithmFormatter 11 | { 12 | 13 | public: 14 | 15 | /** Constructor. */ 16 | AlgorithmFormatter( 17 | const Parameters& parameters, 18 | Output& output): 19 | parameters_(parameters), 20 | output_(output), 21 | os_(parameters.create_os()) { } 22 | 23 | /** Print the header. */ 24 | void start( 25 | const std::string& algorithm_name); 26 | 27 | /** Print the header. */ 28 | void print_header(); 29 | 30 | /** Print current state. */ 31 | void print( 32 | const std::string& s); 33 | 34 | /** Update the solution. */ 35 | void update_solution( 36 | const Solution& solution_new, 37 | const std::string& s); 38 | 39 | /** Update the solution value. */ 40 | void update_value( 41 | Weight value, 42 | const std::string& s); 43 | 44 | /** Update the bound. */ 45 | void update_bound( 46 | Weight bound_new, 47 | const std::string& s); 48 | 49 | /** Method to call at the end of the algorithm. */ 50 | void end(); 51 | 52 | private: 53 | 54 | /** Parameters. */ 55 | const Parameters& parameters_; 56 | 57 | /** Output. */ 58 | Output& output_; 59 | 60 | /** Output stream. */ 61 | std::unique_ptr os_; 62 | 63 | }; 64 | 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /include/knapsacksolver/knapsack/algorithms/dynamic_programming_bellman.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "knapsacksolver/knapsack/solution.hpp" 4 | 5 | namespace knapsacksolver 6 | { 7 | namespace knapsack 8 | { 9 | 10 | Output dynamic_programming_bellman_rec( 11 | const Instance& instance, 12 | const Parameters& parameters = {}); 13 | 14 | 15 | Output dynamic_programming_bellman_array( 16 | const Instance& instance, 17 | const Parameters& parameters = {}); 18 | 19 | 20 | Output dynamic_programming_bellman_array_parallel( 21 | const Instance& instance, 22 | const Parameters& parameters = {}); 23 | 24 | 25 | Output dynamic_programming_bellman_array_all( 26 | const Instance& instance, 27 | const Parameters& parameters = {}); 28 | 29 | 30 | struct DynamicProgrammingBellmanArrayOneOutput: Output 31 | { 32 | DynamicProgrammingBellmanArrayOneOutput( 33 | const Instance& instance): 34 | Output(instance) { } 35 | 36 | 37 | /** Number of iterations. */ 38 | Counter number_of_iterations = 0; 39 | 40 | 41 | virtual void format(std::ostream& os) const override 42 | { 43 | Output::format(os); 44 | int width = format_width(); 45 | os 46 | << std::setw(width) << std::left << "Number of iterations: " << number_of_iterations << std::endl 47 | ; 48 | } 49 | 50 | virtual nlohmann::json to_json() const override 51 | { 52 | nlohmann::json json = Output::to_json(); 53 | json.merge_patch({ 54 | {"NumberOfIterations", number_of_iterations}}); 55 | return json; 56 | } 57 | }; 58 | 59 | const DynamicProgrammingBellmanArrayOneOutput dynamic_programming_bellman_array_one( 60 | const Instance& instance, 61 | const Parameters& parameters = {}); 62 | 63 | 64 | struct DynamicProgrammingBellmanArrayPartParameters: Parameters 65 | { 66 | /** Size of the partial solutions. */ 67 | Counter partial_solution_size = 64; 68 | 69 | 70 | virtual int format_width() const override { return 37; } 71 | 72 | virtual void format(std::ostream& os) const override 73 | { 74 | Parameters::format(os); 75 | int width = format_width(); 76 | os 77 | << std::setw(width) << std::left << "Partial solution size: " << partial_solution_size << std::endl 78 | ; 79 | } 80 | 81 | virtual nlohmann::json to_json() const override 82 | { 83 | nlohmann::json json = Parameters::to_json(); 84 | json.merge_patch({ 85 | {"PartialSolutionSize", partial_solution_size}}); 86 | return json; 87 | } 88 | }; 89 | 90 | struct DynamicProgrammingBellmanArrayPartOutput: Output 91 | { 92 | DynamicProgrammingBellmanArrayPartOutput( 93 | const Instance& instance): 94 | Output(instance) { } 95 | 96 | 97 | /** Number of iterations. */ 98 | Counter number_of_iterations = 0; 99 | 100 | 101 | virtual void format(std::ostream& os) const override 102 | { 103 | Output::format(os); 104 | int width = format_width(); 105 | os 106 | << std::setw(width) << std::left << "Number of iterations: " << number_of_iterations << std::endl 107 | ; 108 | } 109 | 110 | virtual nlohmann::json to_json() const override 111 | { 112 | nlohmann::json json = Output::to_json(); 113 | json.merge_patch({ 114 | {"NumberOfIterations", number_of_iterations}}); 115 | return json; 116 | } 117 | }; 118 | 119 | const DynamicProgrammingBellmanArrayPartOutput dynamic_programming_bellman_array_part( 120 | const Instance& instance, 121 | const DynamicProgrammingBellmanArrayPartParameters& parameters = {}); 122 | 123 | 124 | Output dynamic_programming_bellman_array_rec( 125 | const Instance& instance, 126 | const Parameters& parameters = {}); 127 | 128 | 129 | struct DynamicProgrammingBellmanListParameters: Parameters 130 | { 131 | /** Sort the items. */ 132 | bool sort = false; 133 | 134 | 135 | virtual int format_width() const override { return 37; } 136 | 137 | virtual void format(std::ostream& os) const override 138 | { 139 | Parameters::format(os); 140 | int width = format_width(); 141 | os 142 | << std::setw(width) << std::left << "Sort: " << sort << std::endl 143 | ; 144 | } 145 | 146 | virtual nlohmann::json to_json() const override 147 | { 148 | nlohmann::json json = Parameters::to_json(); 149 | json.merge_patch({ 150 | {"Sort", sort}}); 151 | return json; 152 | } 153 | }; 154 | 155 | Output dynamic_programming_bellman_list( 156 | const Instance& instance, 157 | const DynamicProgrammingBellmanListParameters& parameters = {}); 158 | 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /include/knapsacksolver/knapsack/algorithms/dynamic_programming_primal_dual.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "knapsacksolver/knapsack/solution.hpp" 4 | 5 | namespace knapsacksolver 6 | { 7 | namespace knapsack 8 | { 9 | 10 | struct DynamicProgrammingPrimalDualParameters: Parameters 11 | { 12 | bool greedy = true; 13 | 14 | bool pairing = false; 15 | 16 | ItemId partial_solution_size = 64; 17 | 18 | 19 | virtual int format_width() const override { return 37; } 20 | 21 | virtual void format(std::ostream& os) const override 22 | { 23 | Parameters::format(os); 24 | int width = format_width(); 25 | os 26 | << std::setw(width) << std::left << "Greedy: " << greedy << std::endl 27 | << std::setw(width) << std::left << "Pairing: " << pairing << std::endl 28 | << std::setw(width) << std::left << "Partial solution size: " << partial_solution_size << std::endl 29 | ; 30 | } 31 | 32 | virtual nlohmann::json to_json() const override 33 | { 34 | nlohmann::json json = Parameters::to_json(); 35 | json.merge_patch({ 36 | {"Greedy", greedy}, 37 | {"Pairing", pairing}, 38 | {"PartialSolutionSize", partial_solution_size}}); 39 | return json; 40 | } 41 | }; 42 | 43 | struct DynamicProgrammingPrimalDualOutput: Output 44 | { 45 | DynamicProgrammingPrimalDualOutput( 46 | const Instance& instance): 47 | Output(instance) { } 48 | 49 | 50 | Counter number_of_recursive_calls = 0; 51 | 52 | 53 | virtual void format(std::ostream& os) const override 54 | { 55 | Output::format(os); 56 | int width = format_width(); 57 | os 58 | << std::setw(width) << std::left << "Number of recursive calls: " << number_of_recursive_calls << std::endl 59 | ; 60 | } 61 | 62 | virtual nlohmann::json to_json() const override 63 | { 64 | nlohmann::json json = Output::to_json(); 65 | json.merge_patch({ 66 | {"NumberOfRecursiveCalls", number_of_recursive_calls}}); 67 | return json; 68 | } 69 | }; 70 | 71 | const DynamicProgrammingPrimalDualOutput dynamic_programming_primal_dual( 72 | const Instance& instance, 73 | const DynamicProgrammingPrimalDualParameters& parameters = {}); 74 | 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /include/knapsacksolver/knapsack/algorithms/greedy.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "knapsacksolver/knapsack/solution.hpp" 4 | #include "knapsacksolver/knapsack/sort.hpp" 5 | 6 | namespace knapsacksolver 7 | { 8 | namespace knapsack 9 | { 10 | 11 | Output trivial( 12 | const Instance& instance, 13 | const Parameters& parameters = {}); 14 | 15 | 16 | struct GreedyParameters: Parameters 17 | { 18 | /** Full sort. */ 19 | FullSort* full_sort = nullptr; 20 | 21 | /** Partial sort. */ 22 | PartialSort* partial_sort = nullptr; 23 | 24 | 25 | virtual int format_width() const override { return 37; } 26 | 27 | virtual void format(std::ostream& os) const override 28 | { 29 | Parameters::format(os); 30 | int width = format_width(); 31 | os 32 | << std::setw(width) << std::left << "Has full sort: " << (full_sort != nullptr) << std::endl 33 | << std::setw(width) << std::left << "Has partial sort: " << (partial_sort != nullptr) << std::endl 34 | ; 35 | } 36 | 37 | virtual nlohmann::json to_json() const override 38 | { 39 | nlohmann::json json = Parameters::to_json(); 40 | json.merge_patch({ 41 | {"HasFullSort", (full_sort != nullptr)}, 42 | {"HasPartialSort", (partial_sort != nullptr)}}); 43 | return json; 44 | } 45 | }; 46 | 47 | Output greedy( 48 | const Instance& instance, 49 | const GreedyParameters& parameters = {}); 50 | 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /include/knapsacksolver/knapsack/algorithms/surrogate_relaxation.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "knapsacksolver/knapsack/solution.hpp" 4 | 5 | namespace knapsacksolver 6 | { 7 | namespace knapsack 8 | { 9 | 10 | using SolveCallback = std::function; 11 | 12 | struct SurrogateRelaxationParameters: Parameters 13 | { 14 | SolveCallback solve_callback = [](const Instance& instance) { return Output(instance); }; 15 | }; 16 | 17 | Output surrogate_relaxation( 18 | const Instance& instance, 19 | const SurrogateRelaxationParameters& parameters = {}); 20 | 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /include/knapsacksolver/knapsack/algorithms/upper_bound_dantzig.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "knapsacksolver/knapsack/solution.hpp" 4 | #include "knapsacksolver/knapsack/sort.hpp" 5 | 6 | namespace knapsacksolver 7 | { 8 | namespace knapsack 9 | { 10 | 11 | struct UpperBoundDantzigParameters: Parameters 12 | { 13 | /** Full sort. */ 14 | FullSort* full_sort = nullptr; 15 | 16 | /** Partial sort. */ 17 | PartialSort* partial_sort = nullptr; 18 | 19 | 20 | virtual int format_width() const override { return 37; } 21 | 22 | virtual void format(std::ostream& os) const override 23 | { 24 | Parameters::format(os); 25 | int width = format_width(); 26 | os 27 | << std::setw(width) << std::left << "Has full sort: " << (full_sort != nullptr) << std::endl 28 | << std::setw(width) << std::left << "Has partial sort: " << (partial_sort != nullptr) << std::endl 29 | ; 30 | } 31 | 32 | virtual nlohmann::json to_json() const override 33 | { 34 | nlohmann::json json = Parameters::to_json(); 35 | json.merge_patch({ 36 | {"HasFullSort", (full_sort != nullptr)}, 37 | {"HasPartialSort", (partial_sort != nullptr)}}); 38 | return json; 39 | } 40 | }; 41 | 42 | Output upper_bound_dantzig( 43 | const Instance& instance, 44 | const UpperBoundDantzigParameters& parameters = {}); 45 | 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /include/knapsacksolver/knapsack/generator.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "knapsacksolver/knapsack/instance.hpp" 4 | 5 | #include 6 | 7 | namespace knapsacksolver 8 | { 9 | namespace knapsack 10 | { 11 | 12 | Instance generate_u( 13 | ItemPos number_of_items, 14 | Weight maximum_weight, 15 | Profit maximum_profit, 16 | double capacity_ratio, 17 | std::mt19937_64& generator); 18 | 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /include/knapsacksolver/knapsack/instance.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace knapsacksolver 8 | { 9 | namespace knapsack 10 | { 11 | 12 | using Weight = int64_t; 13 | using Profit = int64_t; 14 | using ItemId = int64_t; 15 | using ItemPos = int64_t; 16 | using StateId = int64_t; 17 | using Counter = int64_t; 18 | using Seed = int64_t; 19 | 20 | /** 21 | * Structure for an item. 22 | */ 23 | struct Item 24 | { 25 | /** Weight of the item. */ 26 | Weight weight = 0; 27 | 28 | /** Profit of the item. */ 29 | Profit profit = 0; 30 | 31 | /* 32 | * Computed attributes 33 | */ 34 | 35 | /** Efficiency of the item. */ 36 | double efficiency = 0.0; 37 | }; 38 | 39 | /** 40 | * Instance class for a knapsack problem. 41 | */ 42 | class Instance 43 | { 44 | 45 | public: 46 | 47 | /* 48 | * Getters 49 | */ 50 | 51 | /** Get the number of items in the instance. */ 52 | inline ItemPos number_of_items() const { return items_.size(); } 53 | 54 | /** Get the capacity of the instance. */ 55 | inline Weight capacity() const { return capacity_; } 56 | 57 | /** Get an item. */ 58 | inline const Item& item(ItemId item_id) const { return items_[item_id]; } 59 | 60 | /** Get the highest item profit. */ 61 | inline Profit highest_item_profit() const { return highest_item_profit_; } 62 | 63 | /** Get the highest item weight. */ 64 | inline Weight highest_item_weight() const { return highest_item_weight_; } 65 | 66 | /** Get the total item profit. */ 67 | inline Profit total_item_profit() const { return total_item_profit_; } 68 | 69 | /** Get the total item weight. */ 70 | inline Weight total_item_weight() const { return total_item_weight_; } 71 | 72 | /** Get the item with the highest efficiency. */ 73 | inline ItemId highest_efficiency_item_id() const { return highest_efficiency_item_id_; } 74 | 75 | /** Get the items sorted by decreasing efficiency. */ 76 | inline std::vector compute_sorted_items() const; 77 | 78 | /* 79 | * Export. 80 | */ 81 | 82 | /** Print the instance into a stream. */ 83 | void format( 84 | std::ostream& os, 85 | int verbosity_level = 1) const; 86 | 87 | /** Write the instance to a file. */ 88 | void write(std::string instance_path) const; 89 | 90 | private: 91 | 92 | /* 93 | * Private methods. 94 | */ 95 | 96 | /** Manual constructor. */ 97 | Instance() { } 98 | 99 | /* 100 | * Private attributes 101 | */ 102 | 103 | /** Items. */ 104 | std::vector items_; 105 | 106 | /** Capacity of the knapsack. */ 107 | Weight capacity_; 108 | 109 | /* 110 | * Commputed attributes 111 | */ 112 | 113 | /** Total item profit. */ 114 | Profit highest_item_profit_ = 0; 115 | 116 | /** Total item weight. */ 117 | Weight highest_item_weight_ = 0; 118 | 119 | /** Total item profit. */ 120 | Profit total_item_profit_ = 0; 121 | 122 | /** Total item weight. */ 123 | Weight total_item_weight_ = 0; 124 | 125 | /** Item with the highest efficiency. */ 126 | ItemId highest_efficiency_item_id_; 127 | 128 | friend class InstanceBuilder; 129 | friend class InstanceFromFloatProfitsBuilder; 130 | 131 | }; 132 | 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /include/knapsacksolver/knapsack/instance_builder.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "knapsacksolver/knapsack/instance.hpp" 4 | 5 | namespace knapsacksolver 6 | { 7 | namespace knapsack 8 | { 9 | 10 | class InstanceBuilder 11 | { 12 | 13 | public: 14 | 15 | /** Constructor. */ 16 | InstanceBuilder() { } 17 | 18 | /** Add an item to the knapsack. */ 19 | void add_item( 20 | Profit profit, 21 | Weight weight); 22 | 23 | /** Set the capacity of the knapsack. */ 24 | void set_capacity(Weight capacity) { instance_.capacity_ = capacity; } 25 | 26 | /** Read an instance from a file. */ 27 | void read( 28 | const std::string& instance_path, 29 | const std::string& format = "standard"); 30 | 31 | /* 32 | * Build 33 | */ 34 | 35 | /** Build. */ 36 | Instance build(); 37 | 38 | private: 39 | 40 | /* 41 | * Private methods 42 | */ 43 | 44 | /* 45 | * Read input file 46 | */ 47 | 48 | /** Read an instance file in 'standard' format. */ 49 | void read_standard(std::ifstream& file); 50 | 51 | /** Read an instance file in 'pisinger' format. */ 52 | void read_pisinger(std::ifstream& file); 53 | 54 | /** Read an instance file in 'jooken' format. */ 55 | void read_jooken(std::ifstream& file); 56 | 57 | /** Read an subset_sum instance file in 'standard' format. */ 58 | void read_subset_sum_standard(std::ifstream& file); 59 | 60 | /* 61 | * Private attributes 62 | */ 63 | 64 | /** Instance. */ 65 | Instance instance_; 66 | 67 | }; 68 | 69 | class InstanceFromFloatProfitsBuilder 70 | { 71 | 72 | public: 73 | 74 | /** Constructor. */ 75 | InstanceFromFloatProfitsBuilder() { } 76 | 77 | /** Add an item to the knapsack. */ 78 | void add_item( 79 | double profit, 80 | Weight weight); 81 | 82 | /** Set the capacity of the knapsack. */ 83 | void set_capacity(Weight capacity) { instance_.capacity_ = capacity; } 84 | 85 | /* 86 | * Build 87 | */ 88 | 89 | /** Build. */ 90 | Instance build(); 91 | 92 | private: 93 | 94 | /* 95 | * Private methods 96 | */ 97 | 98 | /* 99 | * Private attributes 100 | */ 101 | 102 | /** Instance. */ 103 | Instance instance_; 104 | 105 | std::vector profits_double_; 106 | 107 | }; 108 | 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /include/knapsacksolver/knapsack/solution.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "knapsacksolver/knapsack/instance.hpp" 4 | 5 | #include "optimizationtools/utils/utils.hpp" 6 | #include "optimizationtools/utils/output.hpp" 7 | 8 | #include 9 | 10 | namespace knapsacksolver 11 | { 12 | namespace knapsack 13 | { 14 | 15 | /** 16 | * Solution class for a subset sum problem. 17 | */ 18 | class Solution 19 | { 20 | 21 | public: 22 | 23 | /* 24 | * Constructors and destructor 25 | */ 26 | 27 | /** Create an empty solution. */ 28 | Solution(const Instance& instance); 29 | 30 | /** Create a solution from a file. */ 31 | Solution( 32 | const Instance& instance, 33 | std::string certificate_path, 34 | std::string certificate_format = "standard"); 35 | 36 | /** Add an item to the solution. */ 37 | void add(ItemId item_id); 38 | 39 | /** Remove an item to the solution. */ 40 | void remove(ItemId item_id); 41 | 42 | /** Add all items to the solution. */ 43 | void fill(); 44 | 45 | /* 46 | * Getters 47 | */ 48 | 49 | /** Get the instance. */ 50 | inline const Instance& instance() const { return *instance_; } 51 | 52 | /** Get the number of items in the solution. */ 53 | inline ItemPos number_of_items() const { return number_of_items_; } 54 | 55 | /** Get the total weight of the solution. */ 56 | inline Weight weight() const { return weight_; } 57 | 58 | /** Get the total profit of the solution. */ 59 | inline Profit profit() const { return profit_; } 60 | 61 | /** Return 'true' iff the solution contains item 'j'. */ 62 | int8_t contains(ItemId item_id) const { return contains_[item_id]; } 63 | 64 | /** Return 'true' iff the solution is feasible. */ 65 | bool feasible() const { return weight_ <= instance().capacity(); } 66 | 67 | /** Get the total cost of the solution. */ 68 | inline Profit objective_value() const { return profit(); } 69 | 70 | /* 71 | * Export 72 | */ 73 | 74 | /** Write the solution to a file. */ 75 | void write(std::string filepath) const; 76 | 77 | /** Export solution characteristics to a JSON structure. */ 78 | nlohmann::json to_json() const; 79 | 80 | /** Write a formatted output of the instance to a stream. */ 81 | void format( 82 | std::ostream& os, 83 | int verbosity_level = 1) const; 84 | 85 | private: 86 | 87 | /** Instance. */ 88 | const Instance* instance_; 89 | 90 | /** Number of items in the solution. */ 91 | ItemPos number_of_items_ = 0; 92 | 93 | /** Weight of the solution. */ 94 | Weight weight_ = 0; 95 | 96 | /** Profit of the solution. */ 97 | Profit profit_ = 0; 98 | 99 | /** 'contains_[j] == true' iff the solution contains item 'j'. */ 100 | std::vector contains_; 101 | 102 | }; 103 | 104 | /** Stream insertion operator. */ 105 | std::ostream& operator<<(std::ostream& os, const Solution& solution); 106 | 107 | //////////////////////////////////////////////////////////////////////////////// 108 | //////////////////////////////////// Output //////////////////////////////////// 109 | //////////////////////////////////////////////////////////////////////////////// 110 | 111 | inline optimizationtools::ObjectiveDirection objective_direction() 112 | { 113 | return optimizationtools::ObjectiveDirection::Maximize; 114 | } 115 | 116 | /** 117 | * Output structure for a set covering problem. 118 | */ 119 | struct Output: optimizationtools::Output 120 | { 121 | /** Constructor. */ 122 | Output(const Instance& instance): 123 | solution(instance), 124 | bound(instance.total_item_profit()) 125 | { } 126 | 127 | 128 | /** Solution. */ 129 | Solution solution; 130 | 131 | /** Value. */ 132 | Profit value = 0; 133 | 134 | /** Bound. */ 135 | Weight bound = -1; 136 | 137 | /** Elapsed time. */ 138 | double time = 0.0; 139 | 140 | 141 | std::string solution_value() const 142 | { 143 | return optimizationtools::solution_value( 144 | objective_direction(), 145 | solution.feasible(), 146 | value); 147 | } 148 | 149 | double absolute_optimality_gap() const 150 | { 151 | return optimizationtools::absolute_optimality_gap( 152 | objective_direction(), 153 | solution.feasible(), 154 | value, 155 | bound); 156 | } 157 | 158 | double relative_optimality_gap() const 159 | { 160 | return optimizationtools::relative_optimality_gap( 161 | objective_direction(), 162 | solution.feasible(), 163 | value, 164 | bound); 165 | } 166 | 167 | bool has_solution() const { return solution.feasible() && solution.objective_value() == value; } 168 | 169 | virtual nlohmann::json to_json() const 170 | { 171 | return nlohmann::json { 172 | {"Solution", solution.to_json()}, 173 | {"HasSolution", has_solution()}, 174 | {"Value", value}, 175 | {"Bound", bound}, 176 | {"AbsoluteOptimalityGap", absolute_optimality_gap()}, 177 | {"RelativeOptimalityGap", relative_optimality_gap()}, 178 | {"Time", time} 179 | }; 180 | } 181 | 182 | virtual int format_width() const { return 30; } 183 | 184 | virtual void format(std::ostream& os) const 185 | { 186 | int width = format_width(); 187 | os 188 | << std::setw(width) << std::left << "Value: " << value << std::endl 189 | << std::setw(width) << std::left << "Has solution: " << has_solution() << std::endl 190 | << std::setw(width) << std::left << "Bound: " << bound << std::endl 191 | << std::setw(width) << std::left << "Absolute optimality gap: " << absolute_optimality_gap() << std::endl 192 | << std::setw(width) << std::left << "Relative optimality gap (%): " << relative_optimality_gap() * 100 << std::endl 193 | << std::setw(width) << std::left << "Time (s): " << time << std::endl 194 | ; 195 | } 196 | }; 197 | 198 | using NewSolutionCallback = std::function; 199 | 200 | struct Parameters: optimizationtools::Parameters 201 | { 202 | /** Callback function called when a new best solution is found. */ 203 | NewSolutionCallback new_solution_callback = [](const Output&) { }; 204 | 205 | /** Enable json output. */ 206 | bool json_output = false; 207 | 208 | 209 | virtual nlohmann::json to_json() const override 210 | { 211 | nlohmann::json json = optimizationtools::Parameters::to_json(); 212 | json.merge_patch( 213 | {}); 214 | return json; 215 | } 216 | 217 | virtual int format_width() const override { return 23; } 218 | 219 | virtual void format(std::ostream& os) const override 220 | { 221 | optimizationtools::Parameters::format(os); 222 | //int width = format_width(); 223 | //os 224 | // << std::setw(width) << std::left << " Enable: " << reduction_parameters.reduce << std::endl 225 | // ; 226 | } 227 | }; 228 | 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /include/knapsacksolver/knapsack/sort.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "knapsacksolver/knapsack/solution.hpp" 4 | 5 | namespace knapsacksolver 6 | { 7 | namespace knapsack 8 | { 9 | 10 | class FullSort 11 | { 12 | 13 | public: 14 | 15 | /** Constructor. */ 16 | FullSort(const Instance& instance); 17 | 18 | /** Get break solution. */ 19 | const Solution& break_solution() const { return break_solution_; } 20 | 21 | /** Get an item id. */ 22 | ItemId item_id(ItemPos item_pos) const { return sorted_items_[item_pos]; } 23 | 24 | /** Get the break item id. */ 25 | ItemId break_item_id() const { return break_item_id_; } 26 | 27 | /** Get the position of the break item in the sorted items. */ 28 | ItemId break_item_pos() const { return break_item_pos_; } 29 | 30 | private: 31 | 32 | /** Instance. */ 33 | const Instance* instance_; 34 | 35 | /** Break solution. */ 36 | Solution break_solution_; 37 | 38 | /** Sorted items. */ 39 | std::vector sorted_items_; 40 | 41 | /** Break item. */ 42 | ItemId break_item_id_ = -1; 43 | 44 | /** Position of the break item in the sorted items. */ 45 | ItemPos break_item_pos_ = -1; 46 | 47 | }; 48 | 49 | class PartialSort 50 | { 51 | 52 | public: 53 | 54 | /** Constructor. */ 55 | PartialSort(const Instance& instance); 56 | 57 | /** Get instance. */ 58 | const Instance& instance() const { return *instance_; } 59 | 60 | /** Get break solution. */ 61 | const Solution& break_solution() const { return break_solution_; } 62 | 63 | /** Get mandatory items. */ 64 | const Solution& mandatory_items() const { return mandatory_items_; } 65 | 66 | /** Get an item id. */ 67 | ItemId item_id(ItemPos item_pos) const { return sorted_items_[item_pos]; } 68 | 69 | /** Get the break item id. */ 70 | ItemId break_item_id() const { return break_item_id_; } 71 | 72 | /** Get the position of the break item in the sorted items. */ 73 | ItemId break_item_pos() const { return break_item_pos_; } 74 | 75 | /** 76 | * Get the position of the first item in the sorted items. 77 | * 78 | * Once the full sort is completed, items before this position belong to 79 | * the optimal solution. 80 | */ 81 | ItemId first_sorted_item_pos() const { return first_sorted_item_pos_; } 82 | 83 | /** 84 | * Get the position of the last item in the sorted items. 85 | * 86 | * Once the full sort is completed, items after this position don't belong 87 | * to the optimal solution. 88 | */ 89 | ItemId last_sorted_item_pos() const { return last_sorted_item_pos_; } 90 | 91 | /* Get the position of the first item in the left reduced items. */ 92 | ItemPos first_reduced_item_pos() const { return (intervals_left_.empty())? 0: intervals_left_.back().last + 1; } 93 | 94 | /* Get the position of the last item in the right reduced items. */ 95 | ItemPos last_reduced_item_pos() const { return (intervals_right_.empty())? instance().number_of_items() - 1: intervals_right_.back().first - 1; } 96 | 97 | /** Return 'true' iff the left intervals stacks is empty. */ 98 | bool is_intervals_left_empty() const { return intervals_left_.empty(); } 99 | 100 | /** Return 'true' iff the right intervals stacks is empty. */ 101 | bool is_intervals_right_empty() const { return intervals_right_.empty(); } 102 | 103 | ItemId bound_item_left( 104 | ItemPos item_pos, 105 | Profit lower_bound); 106 | 107 | ItemId bound_item_right( 108 | ItemPos item_pos, 109 | Profit lower_bound); 110 | 111 | void move_item_to_core( 112 | ItemPos item_pos, 113 | ItemPos new_item_pos); 114 | 115 | private: 116 | 117 | struct Interval 118 | { 119 | ItemPos first; 120 | ItemPos last; 121 | }; 122 | 123 | /* 124 | * Private methods 125 | */ 126 | 127 | std::pair partition( 128 | ItemPos f, 129 | ItemPos l); 130 | 131 | /** Compute break solution. */ 132 | void compute_break_solution(); 133 | 134 | /** Sort the next left interval. */ 135 | void sort_next_left_interval(Profit lower_bound); 136 | 137 | /** Sort the next right interval. */ 138 | void sort_next_right_interval(Profit lower_bound); 139 | 140 | /** Check the partial sort. */ 141 | bool check() const; 142 | 143 | /** Print the partial sort structures. */ 144 | void format(std::ostream& os) const; 145 | 146 | /* 147 | * Private attributes 148 | */ 149 | 150 | /** Instance. */ 151 | const Instance* instance_; 152 | 153 | /** Break solution. */ 154 | Solution break_solution_; 155 | 156 | /** Mandatory items. */ 157 | Solution mandatory_items_; 158 | 159 | /** Break item. */ 160 | ItemId break_item_id_; 161 | 162 | /** Position of the break item in the sorted items. */ 163 | ItemPos break_item_pos_ = -1; 164 | 165 | /** Sorted items. */ 166 | std::vector sorted_items_; 167 | 168 | /** Stack of unsorted intervals on the left. */ 169 | std::vector intervals_left_; 170 | 171 | /** Stack of unsorted intervals on the right. */ 172 | std::vector intervals_right_; 173 | 174 | /** Position of the first sorted item. */ 175 | ItemPos first_sorted_item_pos_ = -1; 176 | 177 | /** Position of the last sorted item. */ 178 | ItemPos last_sorted_item_pos_ = -1; 179 | 180 | /** Position of the first item of the initial core. */ 181 | ItemPos initial_core_first_item_pos_ = -1; 182 | 183 | /** Position of the last item of the initial core. */ 184 | ItemPos initial_core_last_item_pos_ = -1; 185 | 186 | }; 187 | 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /include/knapsacksolver/knapsack/tests.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "knapsacksolver/knapsack/solution.hpp" 4 | 5 | #include 6 | 7 | namespace knapsacksolver 8 | { 9 | namespace knapsack 10 | { 11 | 12 | std::string get_path(const std::vector& path); 13 | 14 | struct TestInstancePath 15 | { 16 | std::string instance_path; 17 | std::string instance_format; 18 | std::string certificate_path; 19 | std::string certificate_format; 20 | }; 21 | 22 | std::vector get_test_instance_paths(); 23 | 24 | std::vector get_pisinger_instance_paths( 25 | const std::string& s1, 26 | const std::string& s2); 27 | 28 | using Algorithm = std::function; 29 | 30 | struct TestParams 31 | { 32 | Algorithm algorithm; 33 | TestInstancePath files; 34 | }; 35 | 36 | std::vector get_test_params( 37 | const std::vector& algorithms, 38 | const std::vector>& instance_paths); 39 | 40 | const Instance get_instance( 41 | const TestInstancePath& files); 42 | 43 | const Solution get_solution( 44 | const Instance& instance, 45 | const TestInstancePath& files); 46 | 47 | class ExactAlgorithmTest: public testing::TestWithParam { }; 48 | class ExactNoSolutionAlgorithmTest: public testing::TestWithParam { }; 49 | 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /include/knapsacksolver/knapsack/upper_bound.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "knapsacksolver/knapsack/instance.hpp" 4 | 5 | #include 6 | 7 | namespace knapsacksolver 8 | { 9 | namespace knapsack 10 | { 11 | 12 | /** 13 | * Value of the solution if all the remaining capacity is filled with items 14 | * with the efficiency of 'item_id'. 15 | * 16 | * 'current_weight' must be smaller than or equal to 'instance.capacity()'. 17 | */ 18 | inline Profit upper_bound( 19 | const Instance& instance, 20 | Profit current_profit, 21 | Weight current_weight, 22 | ItemId item_id) 23 | { 24 | if (item_id == -1) 25 | return current_profit; 26 | const Item& item = instance.item(item_id); 27 | Profit bound = current_profit + ((instance.capacity() - current_weight) * item.profit) / item.weight; 28 | if (bound < 0) { 29 | throw std::runtime_error( 30 | "current_profit: " + std::to_string(current_profit) 31 | + "; item_id: " + std::to_string(item_id) 32 | + "; efficiency: " + std::to_string(item.efficiency) 33 | + "."); 34 | } 35 | return bound; 36 | } 37 | 38 | /** 39 | * Value of the solution if all the overcapacity is fixed by removing items 40 | * with the efficiency of 'item_id'. 41 | * 42 | * 'current_weight' must be strictly greater than 'instance.capacity()'. 43 | */ 44 | inline Profit upper_bound_reverse( 45 | const Instance& instance, 46 | Profit current_profit, 47 | Weight current_weight, 48 | ItemPos item_id) 49 | { 50 | if (item_id == -1) 51 | return -1; 52 | const Item& item = instance.item(item_id); 53 | Profit bound = current_profit + ((instance.capacity() - current_weight) * item.profit + 1) / item.weight - 1; 54 | if (bound < 0) 55 | throw std::runtime_error(""); 56 | return bound; 57 | } 58 | 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /include/knapsacksolver/multiple_choice_subset_sum/algorithm_formatter.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "knapsacksolver/multiple_choice_subset_sum/solution.hpp" 4 | 5 | namespace knapsacksolver 6 | { 7 | namespace multiple_choice_subset_sum 8 | { 9 | 10 | class AlgorithmFormatter 11 | { 12 | 13 | public: 14 | 15 | /** Constructor. */ 16 | AlgorithmFormatter( 17 | const Parameters& parameters, 18 | Output& output): 19 | parameters_(parameters), 20 | output_(output), 21 | os_(parameters.create_os()) { } 22 | 23 | /** Print the header. */ 24 | void start( 25 | const std::string& algorithm_name); 26 | 27 | /** Print the header. */ 28 | void print_header(); 29 | 30 | /** Print current state. */ 31 | void print( 32 | const std::string& s); 33 | 34 | /** Update the solution. */ 35 | void update_solution( 36 | const Solution& solution_new, 37 | const std::string& s); 38 | 39 | /** Update the solution value. */ 40 | void update_value( 41 | Weight value, 42 | const std::string& s); 43 | 44 | /** Update the bound. */ 45 | void update_bound( 46 | Weight bound_new, 47 | const std::string& s); 48 | 49 | /** Method to call at the end of the algorithm. */ 50 | void end(); 51 | 52 | private: 53 | 54 | /** Parameters. */ 55 | const Parameters& parameters_; 56 | 57 | /** Output. */ 58 | Output& output_; 59 | 60 | /** Output stream. */ 61 | std::unique_ptr os_; 62 | 63 | }; 64 | 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /include/knapsacksolver/multiple_choice_subset_sum/algorithms/dynamic_programming_bellman.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "knapsacksolver/multiple_choice_subset_sum/solution.hpp" 4 | 5 | namespace knapsacksolver 6 | { 7 | namespace multiple_choice_subset_sum 8 | { 9 | 10 | Output dynamic_programming_bellman_array( 11 | const Instance& instance, 12 | const Parameters& parameters = {}); 13 | 14 | Output dynamic_programming_bellman_word_ram( 15 | const Instance& instance, 16 | const Parameters& parameters = {}); 17 | 18 | Output dynamic_programming_bellman_word_ram_rec( 19 | const Instance& instance, 20 | const Parameters& parameters = {}); 21 | 22 | } 23 | } 24 | 25 | -------------------------------------------------------------------------------- /include/knapsacksolver/multiple_choice_subset_sum/instance.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "optimizationtools/utils/output.hpp" 4 | 5 | namespace knapsacksolver 6 | { 7 | namespace multiple_choice_subset_sum 8 | { 9 | 10 | using Weight = int64_t; 11 | using ItemId = int64_t; 12 | using ItemPos = int64_t; 13 | using GroupId = int64_t; 14 | using StateId = int64_t; 15 | using Counter = int64_t; 16 | using Seed = int64_t; 17 | 18 | /** 19 | * Structure for a group of items. 20 | */ 21 | struct Group 22 | { 23 | /** Items. */ 24 | std::vector item_ids; 25 | }; 26 | 27 | /** 28 | * Structure for an item. 29 | */ 30 | struct Item 31 | { 32 | /** Group of the item. */ 33 | GroupId group_id; 34 | 35 | /** Weight of the item. */ 36 | Weight weight; 37 | }; 38 | 39 | /** 40 | * Instance group for a multiple-choice subset sum problem. 41 | */ 42 | class Instance 43 | { 44 | 45 | public: 46 | 47 | /* 48 | * Getters 49 | */ 50 | 51 | /** Get the number of groups in the instance. */ 52 | inline GroupId number_of_groups() const { return groups_.size(); } 53 | 54 | /** Get the number of items in the instance. */ 55 | inline ItemPos number_of_items() const { return items_.size(); } 56 | 57 | /** Get the capacity of the instance. */ 58 | inline Weight capacity() const { return capacity_; } 59 | 60 | /** Get an item. */ 61 | inline const Item& item(ItemId item_id) const { return items_[item_id]; } 62 | 63 | /** Get a group. */ 64 | inline const Group& group(GroupId group_id) const { return groups_[group_id]; } 65 | 66 | /** Get the number of items of a group. */ 67 | inline ItemPos number_of_items(GroupId group_id) const { return groups_[group_id].item_ids.size(); } 68 | 69 | /* 70 | * Export. 71 | */ 72 | 73 | /** Print the instance into a stream. */ 74 | void format( 75 | std::ostream& os, 76 | int verbosity_level = 1) const; 77 | 78 | /** Write the instance to a file. */ 79 | void write(std::string instance_path) const; 80 | 81 | private: 82 | 83 | /* 84 | * Private methods. 85 | */ 86 | 87 | /** Manual constructor. */ 88 | Instance() { } 89 | 90 | /* 91 | * Private attributes 92 | */ 93 | 94 | /** Items. */ 95 | std::vector items_; 96 | 97 | /** Group. */ 98 | std::vector groups_; 99 | 100 | /** Capacity of the knapsack. */ 101 | Weight capacity_; 102 | 103 | friend class InstanceBuilder; 104 | 105 | }; 106 | 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /include/knapsacksolver/multiple_choice_subset_sum/instance_builder.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "knapsacksolver/multiple_choice_subset_sum/instance.hpp" 4 | 5 | namespace knapsacksolver 6 | { 7 | namespace multiple_choice_subset_sum 8 | { 9 | 10 | class InstanceBuilder 11 | { 12 | 13 | public: 14 | 15 | /** Constructor. */ 16 | InstanceBuilder() { } 17 | 18 | /** Add an item to the knapsack. */ 19 | void add_item( 20 | GroupId group_id, 21 | Weight weight); 22 | 23 | /** Set the capacity of the knapsack. */ 24 | void set_capacity(Weight capacity) { instance_.capacity_ = capacity; } 25 | 26 | /** Read an instance from a file. */ 27 | void read( 28 | const std::string& instance_path, 29 | const std::string& format); 30 | 31 | /* 32 | * Build 33 | */ 34 | 35 | /** Build. */ 36 | Instance build(); 37 | 38 | private: 39 | 40 | /* 41 | * Private methods 42 | */ 43 | 44 | /* 45 | * Read input file 46 | */ 47 | 48 | /** Read an instance file in 'standard' format. */ 49 | void read_standard(std::ifstream& file); 50 | 51 | /* 52 | * Private attributes 53 | */ 54 | 55 | /** Instance. */ 56 | Instance instance_; 57 | 58 | }; 59 | 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /include/knapsacksolver/multiple_choice_subset_sum/solution.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "knapsacksolver/multiple_choice_subset_sum/instance.hpp" 4 | 5 | #include "optimizationtools/utils/utils.hpp" 6 | 7 | #include 8 | 9 | namespace knapsacksolver 10 | { 11 | namespace multiple_choice_subset_sum 12 | { 13 | 14 | /** 15 | * Solution group for a multiple-choice subset sum problem. 16 | */ 17 | class Solution 18 | { 19 | 20 | public: 21 | 22 | /* 23 | * Constructors and destructor 24 | */ 25 | 26 | /** Create an empty solution. */ 27 | Solution(const Instance& instance); 28 | 29 | /** Create a solution from a file. */ 30 | Solution(const Instance& instance, std::string certificate_path); 31 | 32 | /** Add an item to the solution. */ 33 | void add(ItemId item_id); 34 | 35 | /* 36 | * Getters 37 | */ 38 | 39 | /** Get the instance. */ 40 | inline const Instance& instance() const { return *instance_; } 41 | 42 | /** Get the number of items in the solution. */ 43 | inline ItemPos number_of_items() const { return number_of_items_; } 44 | 45 | /** Get the total weight of the solution. */ 46 | inline Weight weight() const { return weight_; } 47 | 48 | /** Return 'true' iff the solution contains item 'j'. */ 49 | int8_t contains_item(ItemId item_id) const { return contains_item_[item_id]; } 50 | 51 | /** Return 'true' iff the solution contains an item from group 'group_id'. */ 52 | int8_t contains_group(GroupId group_id) const { return contains_group_[group_id]; } 53 | 54 | /** Return 'true' iff the solution is feasible. */ 55 | bool feasible() const { return weight_ <= instance().capacity(); } 56 | 57 | /** Get the total cost of the solution. */ 58 | inline Weight objective_value() const { return weight(); } 59 | 60 | /* 61 | * Export 62 | */ 63 | 64 | /** Write the solution to a file. */ 65 | void write(std::string filepath) const; 66 | 67 | /** Export solution characteristics to a JSON structure. */ 68 | nlohmann::json to_json() const; 69 | 70 | /** Write a formatted output of the instance to a stream. */ 71 | void format( 72 | std::ostream& os, 73 | int verbosity_level = 1) const; 74 | 75 | private: 76 | 77 | /** Instance. */ 78 | const Instance* instance_; 79 | 80 | /** Number of items in the solution. */ 81 | ItemPos number_of_items_ = 0; 82 | 83 | /** Weight of the solution. */ 84 | Weight weight_ = 0; 85 | 86 | /** 'contains_item_[j] == true' iff the solution contains item 'j'. */ 87 | std::vector contains_item_; 88 | 89 | /** 90 | * 'contains_group_[group_id] == true' iff the solution contains an item 91 | * from group 'group_id'. 92 | */ 93 | std::vector contains_group_; 94 | 95 | }; 96 | 97 | /** Stream insertion operator. */ 98 | std::ostream& operator<<(std::ostream& os, const Solution& solution); 99 | 100 | //////////////////////////////////////////////////////////////////////////////// 101 | //////////////////////////////////// Output //////////////////////////////////// 102 | //////////////////////////////////////////////////////////////////////////////// 103 | 104 | inline optimizationtools::ObjectiveDirection objective_direction() 105 | { 106 | return optimizationtools::ObjectiveDirection::Maximize; 107 | } 108 | 109 | /** 110 | * Output structure for a set covering problem. 111 | */ 112 | struct Output: optimizationtools::Output 113 | { 114 | /** Constructor. */ 115 | Output(const Instance& instance): 116 | solution(instance), 117 | bound(instance.capacity()) 118 | { } 119 | 120 | 121 | /** Solution. */ 122 | Solution solution; 123 | 124 | /** Value. */ 125 | Weight value = 0; 126 | 127 | /** Bound. */ 128 | Weight bound = -1; 129 | 130 | /** Elapsed time. */ 131 | double time = 0.0; 132 | 133 | 134 | std::string solution_value() const 135 | { 136 | return optimizationtools::solution_value( 137 | objective_direction(), 138 | solution.feasible(), 139 | value); 140 | } 141 | 142 | double absolute_optimality_gap() const 143 | { 144 | return optimizationtools::absolute_optimality_gap( 145 | objective_direction(), 146 | solution.feasible(), 147 | value, 148 | bound); 149 | } 150 | 151 | double relative_optimality_gap() const 152 | { 153 | return optimizationtools::relative_optimality_gap( 154 | objective_direction(), 155 | solution.feasible(), 156 | value, 157 | bound); 158 | } 159 | 160 | bool has_solution() const { return solution.feasible() && solution.objective_value() == value; } 161 | 162 | virtual nlohmann::json to_json() const 163 | { 164 | return nlohmann::json { 165 | {"Solution", solution.to_json()}, 166 | {"HasSolution", has_solution()}, 167 | {"Value", value}, 168 | {"Bound", bound}, 169 | {"AbsoluteOptimalityGap", absolute_optimality_gap()}, 170 | {"RelativeOptimalityGap", relative_optimality_gap()}, 171 | {"Time", time} 172 | }; 173 | } 174 | 175 | virtual int format_width() const { return 30; } 176 | 177 | virtual void format(std::ostream& os) const 178 | { 179 | int width = format_width(); 180 | os 181 | << std::setw(width) << std::left << "Value: " << value << std::endl 182 | << std::setw(width) << std::left << "Has solution: " << has_solution() << std::endl 183 | << std::setw(width) << std::left << "Bound: " << bound << std::endl 184 | << std::setw(width) << std::left << "Absolute optimality gap: " << absolute_optimality_gap() << std::endl 185 | << std::setw(width) << std::left << "Relative optimality gap (%): " << relative_optimality_gap() * 100 << std::endl 186 | << std::setw(width) << std::left << "Time (s): " << time << std::endl 187 | ; 188 | } 189 | }; 190 | 191 | using NewSolutionCallback = std::function; 192 | 193 | struct Parameters: optimizationtools::Parameters 194 | { 195 | /** Callback function called when a new best solution is found. */ 196 | NewSolutionCallback new_solution_callback = [](const Output&) { }; 197 | 198 | 199 | virtual nlohmann::json to_json() const override 200 | { 201 | nlohmann::json json = optimizationtools::Parameters::to_json(); 202 | json.merge_patch( 203 | {}); 204 | return json; 205 | } 206 | 207 | virtual int format_width() const override { return 23; } 208 | 209 | virtual void format(std::ostream& os) const override 210 | { 211 | optimizationtools::Parameters::format(os); 212 | //int width = format_width(); 213 | //os 214 | // << std::setw(width) << std::left << " Enable: " << reduction_parameters.reduce << std::endl 215 | // ; 216 | } 217 | }; 218 | 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /include/knapsacksolver/subset_sum/algorithm_formatter.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "knapsacksolver/subset_sum/solution.hpp" 4 | 5 | namespace knapsacksolver 6 | { 7 | namespace subset_sum 8 | { 9 | 10 | class AlgorithmFormatter 11 | { 12 | 13 | public: 14 | 15 | /** Constructor. */ 16 | AlgorithmFormatter( 17 | const Parameters& parameters, 18 | Output& output): 19 | parameters_(parameters), 20 | output_(output), 21 | os_(parameters.create_os()) { } 22 | 23 | /** Print the header. */ 24 | void start( 25 | const std::string& algorithm_name); 26 | 27 | /** Print the header. */ 28 | void print_header(); 29 | 30 | /** Print current state. */ 31 | void print( 32 | const std::string& s); 33 | 34 | /** Update the solution. */ 35 | void update_solution( 36 | const Solution& solution_new, 37 | const std::string& s); 38 | 39 | /** Update the solution value. */ 40 | void update_value( 41 | Weight value, 42 | const std::string& s); 43 | 44 | /** Update the bound. */ 45 | void update_bound( 46 | Weight bound_new, 47 | const std::string& s); 48 | 49 | /** Method to call at the end of the algorithm. */ 50 | void end(); 51 | 52 | private: 53 | 54 | /** Parameters. */ 55 | const Parameters& parameters_; 56 | 57 | /** Output. */ 58 | Output& output_; 59 | 60 | /** Output stream. */ 61 | std::unique_ptr os_; 62 | 63 | }; 64 | 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /include/knapsacksolver/subset_sum/algorithms/dynamic_programming_balancing.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "knapsacksolver/subset_sum/solution.hpp" 4 | 5 | namespace knapsacksolver 6 | { 7 | namespace subset_sum 8 | { 9 | 10 | Output dynamic_programming_balancing_array( 11 | const Instance& instance, 12 | const Parameters& parameters = {}); 13 | 14 | } 15 | } 16 | 17 | -------------------------------------------------------------------------------- /include/knapsacksolver/subset_sum/algorithms/dynamic_programming_bellman.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "knapsacksolver/subset_sum/solution.hpp" 4 | 5 | namespace knapsacksolver 6 | { 7 | namespace subset_sum 8 | { 9 | 10 | struct DynamicProgrammingBellmanArrayOutput: public Output 11 | { 12 | /** Constructor. */ 13 | DynamicProgrammingBellmanArrayOutput(const Instance& instance): 14 | Output(instance) 15 | { } 16 | 17 | std::vector values; 18 | 19 | bool is_reachable(Weight weight) const 20 | { 21 | return values[weight] != -1; 22 | } 23 | }; 24 | 25 | DynamicProgrammingBellmanArrayOutput dynamic_programming_bellman_array( 26 | const Instance& instance, 27 | const Parameters& parameters = {}); 28 | 29 | Output dynamic_programming_bellman_list( 30 | const Instance& instance, 31 | const Parameters& parameters = {}); 32 | 33 | struct DynamicProgrammingBellmanWordRamOutput: public Output 34 | { 35 | /** Constructor. */ 36 | DynamicProgrammingBellmanWordRamOutput(const Instance& instance): 37 | Output(instance) 38 | { } 39 | 40 | std::vector values; 41 | 42 | bool is_reachable(Weight weight) const 43 | { 44 | Weight word = weight / 64; 45 | int bit = weight % 64; 46 | return (values[word] >> bit); 47 | } 48 | }; 49 | 50 | DynamicProgrammingBellmanWordRamOutput dynamic_programming_bellman_word_ram( 51 | const Instance& instance, 52 | const Parameters& parameters = {}); 53 | 54 | Output dynamic_programming_bellman_word_ram_rec( 55 | const Instance& instance, 56 | const Parameters& parameters = {}); 57 | 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /include/knapsacksolver/subset_sum/algorithms/dynamic_programming_primal_dual.hpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fontanf/knapsacksolver/d395f7bcbdde5cdd6e27fadfb2271ae1d0d91c94/include/knapsacksolver/subset_sum/algorithms/dynamic_programming_primal_dual.hpp -------------------------------------------------------------------------------- /include/knapsacksolver/subset_sum/generator.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "knapsacksolver/subset_sum/instance.hpp" 4 | 5 | #include 6 | 7 | namespace knapsacksolver 8 | { 9 | namespace subset_sum 10 | { 11 | 12 | Instance generate_pthree( 13 | ItemPos number_of_items, 14 | std::mt19937_64& generator); 15 | 16 | Instance generate_psix( 17 | ItemPos number_of_items, 18 | std::mt19937_64& generator); 19 | 20 | Instance generate_evenodd( 21 | ItemPos number_of_items, 22 | std::mt19937_64& generator); 23 | 24 | Instance generate_avis( 25 | ItemPos number_of_items); 26 | 27 | Instance generate_todd( 28 | ItemPos number_of_items); 29 | 30 | Instance generate_somatoth( 31 | ItemPos number_of_items, 32 | std::mt19937_64& generator); 33 | 34 | /** 35 | * See: 36 | * "A low-space algorithm for the subset-sum problem on GPU" (Curtis and 37 | * Sanches, 2017) 38 | * https://doi.org/10.1016/j.cor.2017.02.006 39 | */ 40 | Instance generate_evenodd6( 41 | ItemPos number_of_items, 42 | std::mt19937_64& generator); 43 | 44 | /** 45 | * See: 46 | * "A low-space algorithm for the subset-sum problem on GPU" (Curtis and 47 | * Sanches, 2017) 48 | * https://doi.org/10.1016/j.cor.2017.02.006 49 | */ 50 | Instance generate_evenodd8( 51 | ItemPos number_of_items, 52 | std::mt19937_64& generator); 53 | 54 | /** 55 | * See: 56 | * "A low-space algorithm for the subset-sum problem on GPU" (Curtis and 57 | * Sanches, 2017) 58 | * https://doi.org/10.1016/j.cor.2017.02.006 59 | */ 60 | Instance generate_tenfive6( 61 | ItemPos number_of_items, 62 | std::mt19937_64& generator); 63 | 64 | /** 65 | * See: 66 | * "A low-space algorithm for the subset-sum problem on GPU" (Curtis and 67 | * Sanches, 2017) 68 | * https://doi.org/10.1016/j.cor.2017.02.006 69 | */ 70 | Instance generate_tenfive8( 71 | ItemPos number_of_items, 72 | std::mt19937_64& generator); 73 | 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /include/knapsacksolver/subset_sum/instance.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "optimizationtools/utils/output.hpp" 4 | 5 | namespace knapsacksolver 6 | { 7 | namespace subset_sum 8 | { 9 | 10 | using Weight = int64_t; 11 | using ItemId = int64_t; 12 | using ItemPos = int64_t; 13 | using StateId = int64_t; 14 | using Counter = int64_t; 15 | using Seed = int64_t; 16 | 17 | /** 18 | * Instance class for a subset sum problem. 19 | */ 20 | class Instance 21 | { 22 | 23 | public: 24 | 25 | /* 26 | * Getters 27 | */ 28 | 29 | /** Get the number of items in the instance. */ 30 | inline ItemPos number_of_items() const { return weights_.size(); } 31 | 32 | /** Get the capacity of the instance. */ 33 | inline Weight capacity() const { return capacity_; } 34 | 35 | /** Get the weight of an item. */ 36 | inline Weight weight(ItemId item_id) const { return weights_[item_id]; } 37 | 38 | /* 39 | * Export. 40 | */ 41 | 42 | /** Print the instance into a stream. */ 43 | void format( 44 | std::ostream& os, 45 | int verbosity_level = 1) const; 46 | 47 | /** Write the instance to a file. */ 48 | void write(std::string instance_path) const; 49 | 50 | private: 51 | 52 | /* 53 | * Private methods. 54 | */ 55 | 56 | /** Manual constructor. */ 57 | Instance() { } 58 | 59 | /* 60 | * Private attributes 61 | */ 62 | 63 | /** Weights. */ 64 | std::vector weights_; 65 | 66 | /** Capacity of the knapsack. */ 67 | Weight capacity_; 68 | 69 | friend class InstanceBuilder; 70 | 71 | }; 72 | 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /include/knapsacksolver/subset_sum/instance_builder.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "knapsacksolver/subset_sum/instance.hpp" 4 | 5 | namespace knapsacksolver 6 | { 7 | namespace subset_sum 8 | { 9 | 10 | class InstanceBuilder 11 | { 12 | 13 | public: 14 | 15 | /** Constructor. */ 16 | InstanceBuilder() { } 17 | 18 | /** Add an item to the knapsack. */ 19 | void add_item(Weight weight) { instance_.weights_.push_back(weight); }; 20 | 21 | /** Set the capacity of the knapsack. */ 22 | void set_capacity(Weight capacity) { instance_.capacity_ = capacity; } 23 | 24 | /** Read an instance from a file. */ 25 | void read( 26 | const std::string& instance_path, 27 | const std::string& format); 28 | 29 | /* 30 | * Build 31 | */ 32 | 33 | /** Build. */ 34 | Instance build(); 35 | 36 | private: 37 | 38 | /* 39 | * Private methods 40 | */ 41 | 42 | /* 43 | * Read input file 44 | */ 45 | 46 | /** Read an instance file in 'standard' format. */ 47 | void read_standard(std::ifstream& file); 48 | 49 | /* 50 | * Private attributes 51 | */ 52 | 53 | /** Instance. */ 54 | Instance instance_; 55 | 56 | }; 57 | 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /include/knapsacksolver/subset_sum/solution.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "knapsacksolver/subset_sum/instance.hpp" 4 | 5 | #include "optimizationtools/utils/utils.hpp" 6 | 7 | #include 8 | 9 | namespace knapsacksolver 10 | { 11 | namespace subset_sum 12 | { 13 | 14 | /** 15 | * Solution class for a subset sum problem. 16 | */ 17 | class Solution 18 | { 19 | 20 | public: 21 | 22 | /* 23 | * Constructors and destructor 24 | */ 25 | 26 | /** Create an empty solution. */ 27 | Solution(const Instance& instance); 28 | 29 | /** Create a solution from a file. */ 30 | Solution( 31 | const Instance& instance, 32 | const std::string& certificate_path); 33 | 34 | /** Add an item to the solution. */ 35 | void add(ItemId item_id); 36 | 37 | /* 38 | * Getters 39 | */ 40 | 41 | /** Get the instance. */ 42 | inline const Instance& instance() const { return *instance_; } 43 | 44 | /** Get the number of items in the solution. */ 45 | inline ItemPos number_of_items() const { return number_of_items_; } 46 | 47 | /** Get the total weight of the solution. */ 48 | inline Weight weight() const { return weight_; } 49 | 50 | /** Return 'true' iff the solution contains item 'j'. */ 51 | int8_t contains(ItemId item_id) const { return contains_[item_id]; } 52 | 53 | /** Return 'true' iff the solution is feasible. */ 54 | bool feasible() const { return weight_ <= instance().capacity(); } 55 | 56 | /** Get the total cost of the solution. */ 57 | inline Weight objective_value() const { return weight(); } 58 | 59 | /* 60 | * Export 61 | */ 62 | 63 | /** Write the solution to a file. */ 64 | void write(const std::string& certificate_path) const; 65 | 66 | /** Export solution characteristics to a JSON structure. */ 67 | nlohmann::json to_json() const; 68 | 69 | /** Write a formatted output of the instance to a stream. */ 70 | void format( 71 | std::ostream& os, 72 | int verbosity_level = 1) const; 73 | 74 | private: 75 | 76 | /** Instance. */ 77 | const Instance* instance_; 78 | 79 | /** Number of items in the solution. */ 80 | ItemPos number_of_items_ = 0; 81 | 82 | /** Weight of the solution. */ 83 | Weight weight_ = 0; 84 | 85 | /** 'contains_[j] == true' iff the solution contains item 'j'. */ 86 | std::vector contains_; 87 | 88 | }; 89 | 90 | /** Stream insertion operator. */ 91 | std::ostream& operator<<(std::ostream& os, const Solution& solution); 92 | 93 | //////////////////////////////////////////////////////////////////////////////// 94 | //////////////////////////////////// Output //////////////////////////////////// 95 | //////////////////////////////////////////////////////////////////////////////// 96 | 97 | inline optimizationtools::ObjectiveDirection objective_direction() 98 | { 99 | return optimizationtools::ObjectiveDirection::Maximize; 100 | } 101 | 102 | /** 103 | * Output structure for a set covering problem. 104 | */ 105 | struct Output: optimizationtools::Output 106 | { 107 | /** Constructor. */ 108 | Output(const Instance& instance): 109 | solution(instance), 110 | bound(instance.capacity()) 111 | { } 112 | 113 | 114 | /** Solution. */ 115 | Solution solution; 116 | 117 | /** Value. */ 118 | Weight value = 0; 119 | 120 | /** Bound. */ 121 | Weight bound = -1; 122 | 123 | /** Elapsed time. */ 124 | double time = 0.0; 125 | 126 | 127 | std::string solution_value() const 128 | { 129 | return optimizationtools::solution_value( 130 | objective_direction(), 131 | solution.feasible(), 132 | value); 133 | } 134 | 135 | double absolute_optimality_gap() const 136 | { 137 | return optimizationtools::absolute_optimality_gap( 138 | objective_direction(), 139 | solution.feasible(), 140 | value, 141 | bound); 142 | } 143 | 144 | double relative_optimality_gap() const 145 | { 146 | return optimizationtools::relative_optimality_gap( 147 | objective_direction(), 148 | solution.feasible(), 149 | value, 150 | bound); 151 | } 152 | 153 | bool has_solution() const { return solution.feasible() && solution.objective_value() == value; } 154 | 155 | virtual nlohmann::json to_json() const 156 | { 157 | return nlohmann::json { 158 | {"Solution", solution.to_json()}, 159 | {"HasSolution", has_solution()}, 160 | {"Value", value}, 161 | {"Bound", bound}, 162 | {"AbsoluteOptimalityGap", absolute_optimality_gap()}, 163 | {"RelativeOptimalityGap", relative_optimality_gap()}, 164 | {"Time", time} 165 | }; 166 | } 167 | 168 | virtual int format_width() const { return 30; } 169 | 170 | virtual void format(std::ostream& os) const 171 | { 172 | int width = format_width(); 173 | os 174 | << std::setw(width) << std::left << "Value: " << value << std::endl 175 | << std::setw(width) << std::left << "Has solution: " << has_solution() << std::endl 176 | << std::setw(width) << std::left << "Bound: " << bound << std::endl 177 | << std::setw(width) << std::left << "Absolute optimality gap: " << absolute_optimality_gap() << std::endl 178 | << std::setw(width) << std::left << "Relative optimality gap (%): " << relative_optimality_gap() * 100 << std::endl 179 | << std::setw(width) << std::left << "Time (s): " << time << std::endl 180 | ; 181 | } 182 | }; 183 | 184 | using NewSolutionCallback = std::function; 185 | 186 | struct Parameters: optimizationtools::Parameters 187 | { 188 | /** Callback function called when a new best solution is found. */ 189 | NewSolutionCallback new_solution_callback = [](const Output&) { }; 190 | 191 | 192 | virtual nlohmann::json to_json() const override 193 | { 194 | nlohmann::json json = optimizationtools::Parameters::to_json(); 195 | json.merge_patch( 196 | {}); 197 | return json; 198 | } 199 | 200 | virtual int format_width() const override { return 23; } 201 | 202 | virtual void format(std::ostream& os) const override 203 | { 204 | optimizationtools::Parameters::format(os); 205 | //int width = format_width(); 206 | //os 207 | // << std::setw(width) << std::left << " Enable: " << reduction_parameters.reduce << std::endl 208 | // ; 209 | } 210 | }; 211 | 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /include/knapsacksolver/subset_sum/tests.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "knapsacksolver/subset_sum/solution.hpp" 4 | 5 | #include 6 | 7 | namespace knapsacksolver 8 | { 9 | namespace subset_sum 10 | { 11 | 12 | std::string get_path(const std::vector& path); 13 | 14 | struct TestInstancePath 15 | { 16 | std::string instance_path; 17 | std::string instance_format; 18 | std::string certificate_path; 19 | std::string certificate_format; 20 | }; 21 | 22 | std::vector get_pthree_instance_paths( 23 | ItemId number_of_items); 24 | 25 | std::vector get_psix_instance_paths( 26 | ItemId number_of_items); 27 | 28 | using Algorithm = std::function; 29 | 30 | struct TestParams 31 | { 32 | Algorithm algorithm; 33 | TestInstancePath files; 34 | }; 35 | 36 | std::vector get_test_params( 37 | const std::vector& algorithms, 38 | const std::vector>& instance_paths); 39 | 40 | const Instance get_instance( 41 | const TestInstancePath& files); 42 | 43 | const Solution get_solution( 44 | const Instance& instance, 45 | const TestInstancePath& files); 46 | 47 | class ExactAlgorithmTest: public testing::TestWithParam { }; 48 | class ExactNoSolutionAlgorithmTest: public testing::TestWithParam { }; 49 | 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /knapsack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fontanf/knapsacksolver/d395f7bcbdde5cdd6e27fadfb2271ae1d0d91c94/knapsack.png -------------------------------------------------------------------------------- /python/knapsacksolver.cpp: -------------------------------------------------------------------------------- 1 | #include "knapsacksolver/knapsack/algorithms/dynamic_programming_primal_dual.hpp" 2 | #include "knapsacksolver/knapsack/instance_builder.hpp" 3 | 4 | #include 5 | 6 | namespace py = pybind11; 7 | 8 | knapsacksolver::knapsack::Solution solve( 9 | knapsacksolver::knapsack::Instance& instance, 10 | bool verbosity_level) 11 | { 12 | knapsacksolver::knapsack::DynamicProgrammingPrimalDualParameters parameters; 13 | parameters.verbosity_level = verbosity_level; 14 | auto output = knapsacksolver::knapsack::dynamic_programming_primal_dual( 15 | instance, 16 | parameters); 17 | return output.solution; 18 | } 19 | 20 | PYBIND11_MODULE(knapsacksolver, m) 21 | { 22 | py::class_(m, "Instance"); 23 | py::class_(m, "Instance") 24 | .def(py::init<>()) 25 | .def("read", &knapsacksolver::knapsack::InstanceBuilder::read) 26 | .def("set_capacity", &knapsacksolver::knapsack::InstanceBuilder::set_capacity) 27 | .def("add_item", &knapsacksolver::knapsack::InstanceBuilder::add_item); 28 | py::class_(m, "Solution") 29 | .def("contains", &knapsacksolver::knapsack::Solution::contains) 30 | .def("number_of_items", &knapsacksolver::knapsack::Solution::number_of_items) 31 | .def("profit", &knapsacksolver::knapsack::Solution::profit) 32 | .def("weight", &knapsacksolver::knapsack::Solution::weight); 33 | m.def("solve", &solve, 34 | py::arg("instance"), 35 | py::arg("verbosity_level") = 1); 36 | } 37 | -------------------------------------------------------------------------------- /scripts/download_data.py: -------------------------------------------------------------------------------- 1 | import gdown 2 | import os 3 | import pathlib 4 | 5 | 6 | def download(id): 7 | gdown.download(id=id, output="data.7z") 8 | os.system("7z x data.7z -odata") 9 | pathlib.Path("data.7z").unlink() 10 | 11 | download("1jCf-Ye7pAQLVep6MT1HA_G05gdEgWeKk") 12 | download("1tNIfm81yBp7DU3qfQVLLnewYwKlyzsrt") 13 | -------------------------------------------------------------------------------- /scripts/run_tests.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import sys 3 | import os 4 | 5 | parser = argparse.ArgumentParser(description='') 6 | parser.add_argument('directory') 7 | parser.add_argument( 8 | "-t", "--tests", 9 | type=str, 10 | nargs='*', 11 | help='') 12 | 13 | args = parser.parse_args() 14 | 15 | 16 | knapsack_main = os.path.join( 17 | "install", 18 | "bin", 19 | "knapsacksolver_knapsack") 20 | knapsack_data = os.environ['KNAPSACK_DATA'] 21 | 22 | 23 | if args.tests is None or "knapsack-dynamic-programming-primal-dual" in args.tests: 24 | print("Knapack problem / dynamic programming - primal-dual") 25 | print("---------------------------------------------------") 26 | print() 27 | 28 | data = [ 29 | (os.path.join("largecoeff", "knapPI_2_10000_10000000", "knapPI_2_10000_10000000_50.csv"), "pisinger")] 30 | for instance, instance_format in data: 31 | instance_path = os.path.join(knapsack_data, instance) 32 | json_output_path = os.path.join( 33 | args.directory, 34 | "knapsack", 35 | instance + ".json") 36 | if not os.path.exists(os.path.dirname(json_output_path)): 37 | os.makedirs(os.path.dirname(json_output_path)) 38 | command = ( 39 | knapsack_main 40 | + " --verbosity-level 1" 41 | + " --input \"" + instance_path + "\"" 42 | + " --format \"" + instance_format + "\"" 43 | + " --algorithm dynamic-programming-primal-dual" 44 | + " --output \"" + json_output_path + "\"") 45 | print(command) 46 | status = os.system(command) 47 | if status != 0: 48 | sys.exit(1) 49 | print() 50 | print() 51 | print() 52 | 53 | 54 | subset_sum_main = os.path.join( 55 | "install", 56 | "bin", 57 | "knapsacksolver_subset_sum") 58 | subset_sum_data = os.environ['SUBSET_SUM_DATA'] 59 | 60 | 61 | if args.tests is None or "subset-sum-dynamic-programming-bellman-word-ram-rec" in args.tests: 62 | print("Subset sum problem / dynamic programming - Bellman - word RAM - recursive scheme") 63 | print("--------------------------------------------------------------------------------") 64 | print() 65 | 66 | data = [ 67 | (os.path.join("pthree", "pthree_1000_1"), "")] 68 | for instance, instance_format in data: 69 | instance_path = os.path.join(subset_sum_data, instance) 70 | json_output_path = os.path.join( 71 | args.directory, 72 | "subset_sum", 73 | instance) 74 | if not os.path.exists(os.path.dirname(json_output_path)): 75 | os.makedirs(os.path.dirname(json_output_path)) 76 | command = ( 77 | subset_sum_main 78 | + " --verbosity-level 1" 79 | + " --input \"" + instance_path + "\"" 80 | + " --format \"" + instance_format + "\"" 81 | + " --algorithm dynamic-programming-bellman-word-ram-rec" 82 | + " --output \"" + json_output_path + "\"") 83 | print(command) 84 | status = os.system(command) 85 | if status != 0: 86 | sys.exit(1) 87 | print() 88 | print() 89 | print() 90 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(knapsack) 2 | add_subdirectory(subset_sum) 3 | add_subdirectory(multiple_choice_subset_sum) 4 | -------------------------------------------------------------------------------- /src/knapsack/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(KnapsackSolver_knapsack) 2 | target_sources(KnapsackSolver_knapsack PRIVATE 3 | instance.cpp 4 | instance_builder.cpp 5 | solution.cpp 6 | algorithm_formatter.cpp 7 | sort.cpp 8 | upper_bound.cpp) 9 | target_include_directories(KnapsackSolver_knapsack PUBLIC 10 | ${PROJECT_SOURCE_DIR}/include) 11 | target_link_libraries(KnapsackSolver_knapsack PUBLIC 12 | OptimizationTools::utils 13 | OptimizationTools::containers) 14 | add_library(KnapsackSolver::knapsack ALIAS KnapsackSolver_knapsack) 15 | 16 | add_subdirectory(algorithms) 17 | 18 | add_executable(KnapsackSolver_knapsack_main) 19 | target_sources(KnapsackSolver_knapsack_main PRIVATE 20 | main.cpp) 21 | target_link_libraries(KnapsackSolver_knapsack_main PUBLIC 22 | KnapsackSolver_knapsack_greedy 23 | KnapsackSolver_knapsack_dynamic_programming_bellman 24 | KnapsackSolver_knapsack_dynamic_programming_primal_dual 25 | Boost::program_options) 26 | set_target_properties(KnapsackSolver_knapsack_main PROPERTIES OUTPUT_NAME "knapsacksolver_knapsack") 27 | install(TARGETS KnapsackSolver_knapsack_main) 28 | 29 | add_library(KnapsackSolver_knapsack_generator) 30 | target_sources(KnapsackSolver_knapsack_generator PRIVATE 31 | generator.cpp) 32 | target_link_libraries(KnapsackSolver_knapsack_generator PUBLIC 33 | KnapsackSolver_knapsack) 34 | add_library(KnapsackSolver::knapsack::generator ALIAS KnapsackSolver_knapsack_generator) 35 | 36 | add_library(KnapsackSolver_knapsack_tests) 37 | target_sources(KnapsackSolver_knapsack_tests PRIVATE 38 | tests.cpp) 39 | target_link_libraries(KnapsackSolver_knapsack_tests PUBLIC 40 | KnapsackSolver_knapsack 41 | Boost::filesystem 42 | GTest::gtest_main) 43 | add_library(KnapsackSolver::knapsack::tests ALIAS KnapsackSolver_knapsack_tests) 44 | -------------------------------------------------------------------------------- /src/knapsack/algorithm_formatter.cpp: -------------------------------------------------------------------------------- 1 | #include "knapsacksolver/knapsack/algorithm_formatter.hpp" 2 | 3 | #include "optimizationtools/utils/utils.hpp" 4 | 5 | #include 6 | 7 | using namespace knapsacksolver::knapsack; 8 | 9 | void AlgorithmFormatter::start( 10 | const std::string& algorithm_name) 11 | { 12 | if (parameters_.json_output) 13 | output_.json["Parameters"] = parameters_.to_json(); 14 | 15 | if (parameters_.verbosity_level == 0) 16 | return; 17 | *os_ 18 | << "====================================" << std::endl 19 | << " KnapsackSolver " << std::endl 20 | << "====================================" << std::endl 21 | << std::endl 22 | << "Problem" << std::endl 23 | << "-------" << std::endl 24 | << "Knapsack problem" << std::endl 25 | << std::endl 26 | << "Instance" << std::endl 27 | << "--------" << std::endl; 28 | output_.solution.instance().format(*os_, parameters_.verbosity_level); 29 | *os_ 30 | << std::endl 31 | << "Algorithm" << std::endl 32 | << "---------" << std::endl 33 | << algorithm_name << std::endl 34 | << std::endl 35 | << "Parameters" << std::endl 36 | << "----------" << std::endl; 37 | parameters_.format(*os_); 38 | } 39 | 40 | void AlgorithmFormatter::print_header() 41 | { 42 | if (parameters_.verbosity_level == 0) 43 | return; 44 | *os_ 45 | << std::right 46 | << std::endl 47 | << std::setw(12) << "Time (s)" 48 | << std::setw(6) << "Sol." 49 | << std::setw(24) << "Value" 50 | << std::setw(24) << "Bound" 51 | << std::setw(16) << "Gap" 52 | << std::setw(8) << "Gap (%)" 53 | << std::setw(32) << "Comment" 54 | << std::endl 55 | << std::setw(12) << "--------" 56 | << std::setw(6) << "----" 57 | << std::setw(24) << "-----" 58 | << std::setw(24) << "-----" 59 | << std::setw(16) << "---" 60 | << std::setw(8) << "-------" 61 | << std::setw(32) << "-------" 62 | << std::endl; 63 | print(""); 64 | } 65 | 66 | void AlgorithmFormatter::print( 67 | const std::string& s) 68 | { 69 | if (parameters_.verbosity_level == 0) 70 | return; 71 | std::streamsize precision = std::cout.precision(); 72 | *os_ 73 | << std::setw(12) << std::fixed << std::setprecision(3) << output_.time << std::defaultfloat << std::setprecision(precision) 74 | << std::setw(6) << output_.has_solution() 75 | << std::setw(24) << output_.value 76 | << std::setw(24) << output_.bound 77 | << std::setw(16) << output_.absolute_optimality_gap() 78 | << std::setw(8) << std::fixed << std::setprecision(2) << output_.relative_optimality_gap() * 100 << std::defaultfloat << std::setprecision(precision) 79 | << std::setw(32) << s << std::endl; 80 | } 81 | 82 | void AlgorithmFormatter::update_solution( 83 | const Solution& solution_new, 84 | const std::string& s) 85 | { 86 | if ((output_.has_solution() && optimizationtools::is_solution_strictly_better( 87 | objective_direction(), 88 | output_.value, 89 | solution_new.feasible(), 90 | solution_new.objective_value())) 91 | || (!output_.has_solution() && optimizationtools::is_solution_better_or_equal( 92 | objective_direction(), 93 | output_.value, 94 | solution_new.feasible(), 95 | solution_new.objective_value()))) { 96 | output_.time = parameters_.timer.elapsed_time(); 97 | output_.solution = solution_new; 98 | output_.value = output_.solution.objective_value(); 99 | print(s); 100 | if (parameters_.json_output) 101 | output_.json["IntermediaryOutputs"].push_back(output_.to_json()); 102 | parameters_.new_solution_callback(output_); 103 | } 104 | } 105 | 106 | void AlgorithmFormatter::update_value( 107 | Weight value_new, 108 | const std::string& s) 109 | { 110 | if (optimizationtools::is_value_strictly_better( 111 | objective_direction(), 112 | output_.value, 113 | value_new)) { 114 | output_.time = parameters_.timer.elapsed_time(); 115 | output_.value = value_new; 116 | print(s); 117 | if (parameters_.json_output) 118 | output_.json["IntermediaryOutputs"].push_back(output_.to_json()); 119 | parameters_.new_solution_callback(output_); 120 | } 121 | } 122 | 123 | void AlgorithmFormatter::update_bound( 124 | Weight bound_new, 125 | const std::string& s) 126 | { 127 | if (optimizationtools::is_bound_strictly_better( 128 | objective_direction(), 129 | output_.bound, 130 | bound_new)) { 131 | output_.time = parameters_.timer.elapsed_time(); 132 | output_.bound = bound_new; 133 | print(s); 134 | if (parameters_.json_output) 135 | output_.json["IntermediaryOutputs"].push_back(output_.to_json()); 136 | parameters_.new_solution_callback(output_); 137 | } 138 | } 139 | 140 | void AlgorithmFormatter::end() 141 | { 142 | output_.time = parameters_.timer.elapsed_time(); 143 | if (parameters_.json_output) 144 | output_.json["Output"] = output_.to_json(); 145 | 146 | if (parameters_.verbosity_level == 0) 147 | return; 148 | *os_ 149 | << std::endl 150 | << "Final statistics" << std::endl 151 | << "----------------" << std::endl; 152 | output_.format(*os_); 153 | *os_ 154 | << std::endl 155 | << "Solution" << std::endl 156 | << "--------" << std::endl; 157 | output_.solution.format(*os_, parameters_.verbosity_level); 158 | } 159 | -------------------------------------------------------------------------------- /src/knapsack/algorithms/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(KnapsackSolver_knapsack_upper_bound_dantzig) 2 | target_sources(KnapsackSolver_knapsack_upper_bound_dantzig PRIVATE 3 | upper_bound_dantzig.cpp) 4 | target_include_directories(KnapsackSolver_knapsack_upper_bound_dantzig PUBLIC 5 | ${PROJECT_SOURCE_DIR}/include) 6 | target_link_libraries(KnapsackSolver_knapsack_upper_bound_dantzig PUBLIC 7 | KnapsackSolver_knapsack) 8 | add_library(KnapsackSolver::knapsack::upper_bound_dantzig ALIAS KnapsackSolver_knapsack_upper_bound_dantzig) 9 | 10 | add_library(KnapsackSolver_knapsack_greedy) 11 | target_sources(KnapsackSolver_knapsack_greedy PRIVATE 12 | greedy.cpp) 13 | target_include_directories(KnapsackSolver_knapsack_greedy PUBLIC 14 | ${PROJECT_SOURCE_DIR}/include) 15 | target_link_libraries(KnapsackSolver_knapsack_greedy PUBLIC 16 | KnapsackSolver_knapsack) 17 | add_library(KnapsackSolver::knapsack::greedy ALIAS KnapsackSolver_knapsack_greedy) 18 | 19 | find_package(Threads) 20 | add_library(KnapsackSolver_knapsack_dynamic_programming_bellman) 21 | target_sources(KnapsackSolver_knapsack_dynamic_programming_bellman PRIVATE 22 | dynamic_programming_bellman.cpp) 23 | target_include_directories(KnapsackSolver_knapsack_dynamic_programming_bellman PUBLIC 24 | ${PROJECT_SOURCE_DIR}/include) 25 | target_link_libraries(KnapsackSolver_knapsack_dynamic_programming_bellman PUBLIC 26 | KnapsackSolver_knapsack 27 | KnapsackSolver_knapsack_upper_bound_dantzig 28 | KnapsackSolver_knapsack_greedy 29 | Threads::Threads) 30 | add_library(KnapsackSolver::knapsack::dynamic_programming_bellman ALIAS KnapsackSolver_knapsack_dynamic_programming_bellman) 31 | 32 | add_library(KnapsackSolver_knapsack_dynamic_programming_primal_dual) 33 | target_sources(KnapsackSolver_knapsack_dynamic_programming_primal_dual PRIVATE 34 | dynamic_programming_primal_dual.cpp) 35 | target_include_directories(KnapsackSolver_knapsack_dynamic_programming_primal_dual PUBLIC 36 | ${PROJECT_SOURCE_DIR}/include) 37 | target_link_libraries(KnapsackSolver_knapsack_dynamic_programming_primal_dual PUBLIC 38 | KnapsackSolver_knapsack 39 | KnapsackSolver_knapsack_upper_bound_dantzig 40 | KnapsackSolver_knapsack_greedy) 41 | add_library(KnapsackSolver::knapsack::dynamic_programming_primal_dual ALIAS KnapsackSolver_knapsack_dynamic_programming_primal_dual) 42 | -------------------------------------------------------------------------------- /src/knapsack/algorithms/greedy.cpp: -------------------------------------------------------------------------------- 1 | #include "knapsacksolver/knapsack/algorithms/greedy.hpp" 2 | 3 | #include "knapsacksolver/knapsack/algorithm_formatter.hpp" 4 | #include "knapsacksolver/knapsack/sort.hpp" 5 | 6 | using namespace knapsacksolver::knapsack; 7 | 8 | Output knapsacksolver::knapsack::greedy( 9 | const Instance& instance, 10 | const GreedyParameters& parameters) 11 | { 12 | Output output(instance); 13 | AlgorithmFormatter algorithm_formatter(parameters, output); 14 | algorithm_formatter.start("Greedy"); 15 | 16 | // Check trivial cases. 17 | if (instance.total_item_weight() <= instance.capacity()) { 18 | Solution solution(instance); 19 | solution.fill(); 20 | 21 | // Update solution. 22 | algorithm_formatter.update_solution(solution, "all items fit"); 23 | // Update bound. 24 | algorithm_formatter.update_bound(solution.profit(), "all items fit"); 25 | 26 | algorithm_formatter.end(); 27 | return output; 28 | } 29 | 30 | Solution solution_forward(instance); 31 | Solution solution_backward(instance); 32 | 33 | if (parameters.full_sort != nullptr) { 34 | solution_forward = parameters.full_sort->break_solution(); 35 | solution_backward = parameters.full_sort->break_solution(); 36 | solution_backward.add(parameters.full_sort->break_item_id()); 37 | } else if (parameters.partial_sort != nullptr) { 38 | solution_forward = parameters.partial_sort->break_solution(); 39 | solution_backward = parameters.partial_sort->break_solution(); 40 | solution_backward.add(parameters.partial_sort->break_item_id()); 41 | } else { 42 | PartialSort partial_sort(instance); 43 | solution_forward = partial_sort.break_solution(); 44 | solution_backward = partial_sort.break_solution(); 45 | solution_backward.add( partial_sort.break_item_id()); 46 | } 47 | 48 | if (solution_forward.weight() > instance.capacity()) { 49 | throw std::logic_error("forward"); 50 | } 51 | 52 | if (solution_backward.weight() < instance.capacity()) { 53 | throw std::logic_error("backward"); 54 | } 55 | 56 | // Find the best item to add to the forward solution and the best item to 57 | // remove from the backward solution. 58 | ItemId item_id_best_forward = -1; 59 | ItemId item_id_best_backward = -1; 60 | for (ItemId item_id = 0; 61 | item_id < instance.number_of_items(); 62 | ++item_id) { 63 | const Item& item = instance.item(item_id); 64 | // Forward. 65 | if (!solution_forward.contains(item_id)) { 66 | if (solution_forward.weight() + item.weight 67 | <= instance.capacity()) { 68 | if (item_id_best_forward == -1 69 | || instance.item(item_id_best_forward).profit 70 | < item.profit) { 71 | item_id_best_forward = item_id; 72 | } 73 | } 74 | } 75 | // Backward. 76 | if (solution_backward.contains(item_id)) { 77 | if (solution_forward.weight() - item.weight 78 | <= instance.capacity()) { 79 | if (item_id_best_backward == -1 80 | || instance.item(item_id_best_backward).profit 81 | > item.profit) { 82 | item_id_best_backward = item_id; 83 | } 84 | } 85 | } 86 | } 87 | if (item_id_best_forward != -1) 88 | solution_forward.add(item_id_best_forward); 89 | algorithm_formatter.update_solution(solution_forward, "forward"); 90 | 91 | if (item_id_best_backward != -1) { 92 | solution_backward.remove(item_id_best_backward); 93 | algorithm_formatter.update_solution(solution_backward, "backward"); 94 | } 95 | 96 | algorithm_formatter.end(); 97 | return output; 98 | } 99 | -------------------------------------------------------------------------------- /src/knapsack/algorithms/greedy_test.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fontanf/knapsacksolver/d395f7bcbdde5cdd6e27fadfb2271ae1d0d91c94/src/knapsack/algorithms/greedy_test.cpp -------------------------------------------------------------------------------- /src/knapsack/algorithms/surrogate_relaxation.cpp: -------------------------------------------------------------------------------- 1 | #include "knapsacksolver/knapsack/algorithms/surrogate_relaxation.hpp" 2 | 3 | #include "knapsacksolver/knapsack/algorithm_formatter.hpp" 4 | #include "knapsacksolver/knapsack/algorithms/upper_bound_dantzig.hpp" 5 | #include "knapsacksolver/knapsack/algorithms/dynamic_programming_primal_dual.hpp" 6 | 7 | #include 8 | 9 | using namespace knapsacksolver::knapsack; 10 | 11 | ItemId maximum_cardinality( 12 | const Instance& instance) 13 | { 14 | if (instance.number_of_items() == 1) { 15 | return 1; 16 | } 17 | 18 | std::vector sorted_items(instance.number_of_items()); 19 | std::iota(sorted_items.begin(), sorted_items.end(), 0); 20 | ItemId f = 0; 21 | ItemId l = instance.number_of_items() - 1; 22 | Weight w = 0; 23 | while (f < l) { 24 | if (l - f < 128) { 25 | std::sort( 26 | sorted_items.begin() + f, 27 | sorted_items.begin() + l + 1, 28 | [&instance](ItemPos item_id_1, ItemPos item_id_2) { 29 | return instance.item(item_id_1).weight < instance.item(item_id_2).weight;}); 30 | break; 31 | } 32 | 33 | ItemPos pivot_item_pos = f + 1 + (l - f) / 2; 34 | 35 | iter_swap(sorted_items.begin() + pivot_item_pos, sorted_items.begin() + l); 36 | ItemId item_pos = f; 37 | for (ItemPos item_pos_2 = f; item_pos_2 < l; ++item_pos_2) { 38 | if (instance.item(sorted_items[item_pos_2]).weight > instance.item(sorted_items[l]).weight) 39 | continue; 40 | iter_swap(sorted_items.begin() + item_pos_2, sorted_items.begin() + item_pos); 41 | item_pos++; 42 | } 43 | iter_swap(sorted_items.begin() + item_pos, sorted_items.begin() + l); 44 | 45 | Weight w_curr = w; 46 | for (ItemId i = f; i < item_pos; ++i) 47 | w_curr += instance.item(sorted_items[i]).weight; 48 | 49 | if (w_curr + instance.item(sorted_items[item_pos]).weight 50 | <= instance.capacity()) { 51 | f = item_pos + 1; 52 | w = w_curr + instance.item(sorted_items[item_pos]).weight; 53 | } else if (w_curr > instance.capacity()) { 54 | l = item_pos - 1; 55 | } else { 56 | break; 57 | } 58 | } 59 | 60 | ItemPos k = 0; 61 | Weight weight = 0; 62 | for (ItemPos item_pos = 0; 63 | item_pos < instance.number_of_items(); 64 | ++item_pos) { 65 | weight += instance.item(sorted_items[item_pos]).weight; 66 | if (weight > instance.capacity()) { 67 | k = item_pos; 68 | break; 69 | } 70 | } 71 | 72 | return k; 73 | } 74 | 75 | ItemId minimum_cardinality( 76 | const Instance& instance, 77 | Profit lower_bound) 78 | { 79 | if (instance.number_of_items() <= 1) { 80 | return 1; 81 | } 82 | 83 | std::vector sorted_items(instance.number_of_items()); 84 | std::iota(sorted_items.begin(), sorted_items.end(), 0); 85 | ItemId f = 0; 86 | ItemId l = instance.number_of_items() - 1; 87 | Profit p = 0; 88 | while (f < l) { 89 | if (l - f < 128) { 90 | std::sort( 91 | sorted_items.begin() + f, 92 | sorted_items.begin() + l + 1, 93 | [&instance](ItemPos item_id_1, ItemPos item_id_2) { 94 | return instance.item(item_id_1).profit > instance.item(item_id_2).profit;}); 95 | break; 96 | } 97 | 98 | ItemPos pivot_item_pos = f + 1 + (l - f) / 2; 99 | 100 | iter_swap(sorted_items.begin() + pivot_item_pos, sorted_items.begin() + l); 101 | ItemId item_pos = f; 102 | for (ItemId item_pos_2 = f; item_pos_2 < l; ++item_pos_2) { 103 | if (instance.item(sorted_items[item_pos_2]).profit < instance.item(sorted_items[l]).profit) 104 | continue; 105 | iter_swap(sorted_items.begin() + item_pos_2, sorted_items.begin() + item_pos); 106 | item_pos++; 107 | } 108 | iter_swap(sorted_items.begin() + item_pos, sorted_items.begin() + l); 109 | 110 | Profit p_curr = p; 111 | for (ItemId item_pos_2 = f; item_pos_2 < item_pos; ++item_pos_2) 112 | p_curr += instance.item(sorted_items[item_pos_2]).profit; 113 | 114 | if (p_curr > lower_bound) { 115 | l = item_pos - 1; 116 | } else if (p_curr + instance.item(sorted_items[item_pos]).profit <= lower_bound) { 117 | f = item_pos + 1; 118 | p = p_curr + instance.item(sorted_items[item_pos]).profit; 119 | } else { 120 | break; 121 | } 122 | } 123 | 124 | ItemPos k = -1; 125 | Profit profit = 0; 126 | for (ItemPos item_pos = 0; item_pos < instance.number_of_items(); ++item_pos) { 127 | profit += instance.item(sorted_items[item_pos]).profit; 128 | if (profit > lower_bound) { 129 | k = item_pos + 1; 130 | break; 131 | } 132 | } 133 | 134 | return k; 135 | } 136 | 137 | struct UBS 138 | { 139 | Profit ub; 140 | Weight s; 141 | }; 142 | 143 | UBS surrogate_solve( 144 | const Instance& instance, 145 | ItemId k, 146 | Weight s_min, 147 | Weight s_max) 148 | { 149 | FFOT_LOG_FOLD_START(logger, "surrogate_solve k " << k << " s_min " << s_min << " s_max " << s_max << std::endl); 150 | ItemPos first = 0; 151 | Profit ub = upper_bound_dantzig(instance).bound; 152 | Weight s_prec = 0; 153 | Weight s = 0; 154 | Weight s_best = 0; 155 | Weight s1 = s_min; 156 | Weight s2 = s_max; 157 | Weight wabs = instance.highest_item_profit(); 158 | Weight wlim = INT_FAST64_MAX / instance.highest_item_profit(); 159 | 160 | while (s1 <= s2) { 161 | s = (s1 + s2) / 2; 162 | FFOT_LOG_FOLD_START(logger, "s1 " << s1 << " s " << s << " s2 " << s2 << std::endl); 163 | 164 | // Avoid INT overflow 165 | if (s_min == 0 && s != 0) { 166 | if (INT_FAST64_MAX / s < k 167 | || instance.capacity() > INT_FAST64_MAX - s * k 168 | || INT_FAST64_MAX / instance.number_of_items() < wmax + s 169 | || wmax + s > wlim) { 170 | s2 = s - 1; 171 | FFOT_LOG_FOLD_END(logger, ""); 172 | continue; 173 | } else { 174 | wmax += s - s_prec; 175 | } 176 | } 177 | if (s_max == 0 && s != 0) { 178 | wabs = (wmax+s > -wmin+s)? wmax+s: wmin+s; 179 | if (INT_FAST64_MAX / -s < k 180 | || INT_FAST64_MAX / instance.number_of_items() < wabs 181 | || wabs > wlim) { 182 | s1 = s + 1; 183 | FFOT_LOG_FOLD_END(logger, ""); 184 | continue; 185 | } else { 186 | wmax += s - s_prec; 187 | wmin += s - s_prec; 188 | } 189 | } 190 | 191 | instance.surrogate(s - s_prec, k, first FFOT_DBG(FFOT_COMMA logger)); 192 | Profit p = upper_bound_dantzig(instance); 193 | ItemPos b = instance.break_item(); 194 | 195 | if (ub > p) { 196 | ub = p; 197 | s_best = s; 198 | } 199 | 200 | if (b == k && instance.break_capacity() == 0) { 201 | break; 202 | } 203 | 204 | if ((s_min == 0 && b >= k) || (s_max == 0 && b >= k)) { 205 | s1 = s + 1; 206 | } else { 207 | s2 = s - 1; 208 | } 209 | s_prec = s; 210 | } 211 | 212 | return {ub, s_best}; 213 | } 214 | 215 | Output knapsacksolver::knapsack::surrogate_relaxation( 216 | const Instance& instance, 217 | const SurrogateRelaxationParameters& parameters) 218 | { 219 | Output output(instance); 220 | AlgorithmFormatter algorithm_formatter(parameters, output); 221 | algorithm_formatter.start("Surrogate relaxation"); 222 | FFOT_DBG(std::shared_ptr logger = parameters.get_logger();) 223 | 224 | FFOT_LOG_FOLD_START(logger, "surrogate relaxation lb " << output.lower_bound << std::endl); 225 | 226 | instance.sort_partially(FFOT_DBG(info)); 227 | ItemPos b = instance.break_item(); 228 | 229 | // Trivial cases 230 | if (instance.reduced_number_of_items() == 0) { 231 | Profit ub = (instance.reduced_solution() == NULL)? 232 | 0: 233 | instance.reduced_solution()->profit(); 234 | if (output.upper_bound == -1 || output.upper_bound > ub) { 235 | algorithm_formatter.update_bound( 236 | ub, 237 | "surrogate relaxation"); 238 | } 239 | FFOT_LOG_FOLD_END(logger, "no items"); 240 | return; 241 | } 242 | Profit ub = upper_bound_dantzig(instance); 243 | if (instance.break_capacity() == 0 || b == instance.last_item() + 1) { 244 | if (output.upper_bound == -1 || output.upper_bound > ub) { 245 | algorithm_formatter.update_bound( 246 | ub, 247 | "surrogate relaxation"); 248 | } 249 | FFOT_LOG_FOLD_END(logger, "dantzig"); 250 | return; 251 | } 252 | 253 | // Compte s_min and s_max 254 | // s_min and s_max should ideally be (-)pmax*wmax, but this may cause 255 | // overflow 256 | Weight wmax = instance.item(instance.max_weight_item(FFOT_DBG(info))).w; 257 | Profit pmax = instance.item(instance.max_profit_item(FFOT_DBG(info))).p; 258 | Weight s_max = (INT_FAST64_MAX / pmax > wmax)? pmax*wmax: INT_FAST64_MAX; 259 | Weight s_min = (INT_FAST64_MAX / pmax > wmax)? -pmax*wmax: -INT_FAST64_MAX; 260 | 261 | if (maximum_cardinality(instance FFOT_DBG(FFOT_COMMA info)) == b) { 262 | UBS o = surrogate_solve(instance, b, 0, s_max, end FFOT_DBG(FFOT_COMMA info)); 263 | if (*end) 264 | return; 265 | Profit ub = std::max(o.ub, output.lower_bound); 266 | algorithm_formatter.update_bound( 267 | ub, 268 | "surrogate relaxation"); 269 | if (output.upper_bound == output.lower_bound || o.s == 0) 270 | return; 271 | 272 | instance.surrogate(o.s, b FFOT_DBG(FFOT_COMMA info)); 273 | Output output0 = func(instance, Info(info, false, ""), end); 274 | if (output0.solution.profit() != output0.upper_bound) 275 | return; 276 | Solution sol_sur(output.solution.instance()); 277 | sol_sur.update(output0.solution); 278 | algorithm_formatter.update_solution( 279 | sol_sur, 280 | "surrogate ins res (lb)"); 281 | ub = std::max(sol_sur.profit(), output.lower_bound); 282 | algorithm_formatter.update_bound( 283 | ub, 284 | "surrogate ins res (ub)"); 285 | } else if (minimum_cardinality(instance, output.lower_bound FFOT_DBG(FFOT_COMMA info)) == b + 1) { 286 | UBS o = surrogate_solve(instance, b + 1, s_min, 0, end FFOT_DBG(FFOT_COMMA info)); 287 | if (*end) 288 | return; 289 | Profit ub = std::max(o.ub, output.lower_bound); 290 | algorithm_formatter.update_bound( 291 | ub, 292 | "surrogate relaxation"); 293 | if (output.upper_bound == output.lower_bound || o.s == 0) 294 | return; 295 | 296 | instance.surrogate(o.s, b + 1 FFOT_DBG(FFOT_COMMA info)); 297 | Output output0 = func(instance, Info(info, false, ""), end); 298 | if (output0.solution.profit() != output0.upper_bound) 299 | return; 300 | Solution sol_sur(output.solution.instance()); 301 | sol_sur.update(output0.solution); 302 | algorithm_formatter.update_solution(sol_sur, "surrogate ins res (lb)"); 303 | ub = std::max(sol_sur.profit(), output.lower_bound); 304 | algorithm_formatter.update_bound(ub, "surrogate ins res (ub)"); 305 | } else { 306 | Instance instance_2(instance); 307 | UBS o1 = surrogate_solve( 308 | instance, 309 | b, 310 | 0, 311 | s_max, 312 | end 313 | FFOT_DBG(FFOT_COMMA info)); 314 | if (*end) 315 | return; 316 | UBS o2 = surrogate_solve( 317 | instance_2, 318 | b + 1, 319 | s_min, 320 | 0, 321 | end 322 | FFOT_DBG(FFOT_COMMA info)); 323 | if (*end) 324 | return; 325 | //std::cout << o1.ub << " " << o2.ub << " " << output.lower_bound << std::endl; 326 | Profit ub = std::max(std::max(o1.ub, o2.ub), output.lower_bound); 327 | algorithm_formatter.update_bound(ub, "surrogate relaxation"); 328 | if (output.upper_bound == output.lower_bound || o1.s == 0 || o2.s == 0) 329 | return; 330 | 331 | instance.surrogate(o1.s, b FFOT_DBG(FFOT_COMMA info)); 332 | Output output1 = func(instance, Info(info, false, ""), end); 333 | if (output1.solution.profit() != output1.upper_bound) 334 | return; 335 | Solution sol_sur1(output.solution.instance()); 336 | sol_sur1.update(output1.solution); 337 | algorithm_formatter.update_solution(sol_sur1, "surrogate ins res (lb)"); 338 | if (*end || output.lower_bound == output.upper_bound) 339 | return; 340 | 341 | instance_2.surrogate(o2.s, b + 1 FFOT_DBG(FFOT_COMMA info)); 342 | Output output2 = func(instance_2, Info(info, false, ""), end); 343 | if (output2.solution.profit() != output2.upper_bound) 344 | return; 345 | Solution sol_sur2(output.solution.instance()); 346 | sol_sur2.update(output2.solution); 347 | algorithm_formatter.update_solution(sol_sur2, "surrogate ins res (lb)"); 348 | 349 | ub = std::max( 350 | std::max(sol_sur1.profit(), sol_sur2.profit()), 351 | output.lower_bound); 352 | algorithm_formatter.update_bound(ub, "surrogate ins res (ub)"); 353 | } 354 | 355 | FFOT_LOG_FOLD_END(logger, ""); 356 | } 357 | -------------------------------------------------------------------------------- /src/knapsack/algorithms/upper_bound_dantzig.cpp: -------------------------------------------------------------------------------- 1 | #include "knapsacksolver/knapsack/algorithms/upper_bound_dantzig.hpp" 2 | 3 | #include "knapsacksolver/knapsack/algorithm_formatter.hpp" 4 | #include "knapsacksolver/knapsack/upper_bound.hpp" 5 | 6 | using namespace knapsacksolver::knapsack; 7 | 8 | Output knapsacksolver::knapsack::upper_bound_dantzig( 9 | const Instance& instance, 10 | const UpperBoundDantzigParameters& parameters) 11 | { 12 | Output output(instance); 13 | AlgorithmFormatter algorithm_formatter(parameters, output); 14 | algorithm_formatter.start("Dantzig upper bound"); 15 | 16 | // Check trivial cases. 17 | if (instance.total_item_weight() <= instance.capacity()) { 18 | // Update bound. 19 | algorithm_formatter.update_bound( 20 | instance.total_item_profit(), 21 | "all items fit"); 22 | 23 | algorithm_formatter.end(); 24 | return output; 25 | } 26 | 27 | Profit upper_bound_curr = -1; 28 | if (parameters.full_sort != nullptr) { 29 | upper_bound_curr = upper_bound( 30 | instance, 31 | parameters.full_sort->break_solution().profit(), 32 | parameters.full_sort->break_solution().weight(), 33 | parameters.full_sort->break_item_id()); 34 | } else if (parameters.partial_sort != nullptr) { 35 | upper_bound_curr = upper_bound( 36 | instance, 37 | parameters.partial_sort->break_solution().profit(), 38 | parameters.partial_sort->break_solution().weight(), 39 | parameters.partial_sort->break_item_id()); 40 | } else { 41 | PartialSort partial_sort(instance); 42 | upper_bound_curr = upper_bound( 43 | instance, 44 | partial_sort.break_solution().profit(), 45 | partial_sort.break_solution().weight(), 46 | partial_sort.break_item_id()); 47 | } 48 | 49 | // Update bound. 50 | algorithm_formatter.update_bound( 51 | upper_bound_curr, 52 | "algorithm end (bound)"); 53 | 54 | algorithm_formatter.end(); 55 | return output; 56 | } 57 | -------------------------------------------------------------------------------- /src/knapsack/generator.cpp: -------------------------------------------------------------------------------- 1 | #include "knapsacksolver/knapsack/generator.hpp" 2 | 3 | #include "knapsacksolver/knapsack/instance_builder.hpp" 4 | 5 | #include 6 | 7 | using namespace knapsacksolver::knapsack; 8 | 9 | Instance knapsacksolver::knapsack::generate_u( 10 | ItemPos number_of_items, 11 | Weight maximum_weight, 12 | Profit maximum_profit, 13 | double capacity_ratio, 14 | std::mt19937_64& generator) 15 | { 16 | InstanceBuilder instance_builder; 17 | 18 | // Add items. 19 | Weight weight_max = 0; 20 | Weight weight_sum = 0; 21 | for (ItemPos pos = 0; pos < number_of_items; ++pos) { 22 | std::uniform_int_distribution distribution_weight(1, maximum_weight); 23 | std::uniform_int_distribution distribution_profit(1, maximum_profit); 24 | Weight weight = distribution_weight(generator); 25 | Profit profit = distribution_profit(generator); 26 | instance_builder.add_item(profit, weight); 27 | 28 | weight_max = std::max(weight_max, weight); 29 | weight_sum += weight; 30 | } 31 | 32 | // Compute capacity. 33 | Weight capacity = std::max( 34 | weight_max, 35 | (Weight)(capacity_ratio * weight_sum)); 36 | instance_builder.set_capacity(capacity); 37 | 38 | return instance_builder.build(); 39 | } 40 | -------------------------------------------------------------------------------- /src/knapsack/generator_main.cpp: -------------------------------------------------------------------------------- 1 | #include "knapsacksolver/knapsack/generator.hpp" 2 | 3 | #include 4 | 5 | using namespace knapsacksolver::knapsack; 6 | 7 | int main(int argc, char *argv[]) 8 | { 9 | namespace po = boost::program_options; 10 | 11 | // Parse program options 12 | Generator data; 13 | std::string output_file = ""; 14 | std::string plot_file = ""; 15 | po::options_description desc("Allowed options"); 16 | desc.add_options() 17 | ("help,h", "produce help message") 18 | (",t", po::value(&data.t)->required(), "set instancetance type (u, wc, sc, isc, asc, ss, sw, mstr, pceil, circle)") 19 | (",n", po::value(&data.n)->required(), "set item number") 20 | (",r", po::value(&data.r), "set R") 21 | ("ka", po::value(&data.k1), "set k1 (for mstr instancetances)") 22 | ("kb", po::value(&data.k2), "set k2 (for mstr instancetances)") 23 | (",d", po::value(&data.d), "set d (for mstr (6), pceil (3) and circle (3/2) instancetances)") 24 | (",H", po::value(&data.h), "set h") 25 | ("hmax", po::value(&data.hmax), "set hmax") 26 | (",x", po::value(&data.x), "set x") 27 | (",s", po::value(&data.s), "set seed") 28 | ("spanner", "set spanner") 29 | ("normal", "set normal") 30 | ("dw", po::value(&data.dw), "set dw") 31 | (",m", po::value(&data.m), "set m (for spanner instancetances)") 32 | (",v", po::value(&data.v), "set v (for spanner instancetances)") 33 | (",o", po::value(&output_file), "set output file") 34 | (",p", po::value(&plot_file), "set plot file") 35 | ; 36 | po::variables_map vm; 37 | po::store(po::parse_command_line(argc, argv, desc), vm); 38 | if (vm.count("help")) { 39 | std::cout << desc << std::endl;; 40 | return 1; 41 | } 42 | try { 43 | po::notify(vm); 44 | } catch (const po::required_option& e) { 45 | std::cout << desc << std::endl;; 46 | return 1; 47 | } 48 | data.spanner = vm.count("spanner"); 49 | data.normal = vm.count("normal"); 50 | 51 | std::cout << data << std::endl; 52 | Instance instance = data.generate(); 53 | 54 | if (plot_file != "") 55 | instance.plot(plot_file); 56 | if (output_file != "") 57 | instance.write(output_file); 58 | 59 | return 0; 60 | } 61 | 62 | -------------------------------------------------------------------------------- /src/knapsack/instance.cpp: -------------------------------------------------------------------------------- 1 | #include "knapsacksolver/knapsack/instance.hpp" 2 | 3 | #include 4 | #include 5 | 6 | using namespace knapsacksolver::knapsack; 7 | 8 | void Instance::format( 9 | std::ostream& os, 10 | int verbosity_level) const 11 | { 12 | if (verbosity_level >= 1) { 13 | os 14 | << "Number of items: " << number_of_items() << std::endl 15 | << "Capacity: " << capacity() << std::endl 16 | << "Highest item profit: " << highest_item_profit() << std::endl 17 | << "Highest item weight: " << highest_item_weight() << std::endl 18 | << "Total item profit: " << total_item_profit() << std::endl 19 | << "Total item weight: " << total_item_weight() << std::endl 20 | << "Weight ratio: " << (double)total_item_weight() / capacity() << std::endl 21 | ; 22 | } 23 | 24 | if (verbosity_level >= 2) { 25 | os 26 | << std::endl 27 | << std::setw(12) << "Item" 28 | << std::setw(12) << "Weight" 29 | << std::setw(24) << "Profit" 30 | << std::setw(16) << "Eff." 31 | << std::endl 32 | << std::setw(12) << "----" 33 | << std::setw(12) << "------" 34 | << std::setw(24) << "------" 35 | << std::setw(16) << "----" 36 | << std::endl; 37 | for (ItemId item_id = 0; item_id < number_of_items(); ++item_id) { 38 | const Item& item = this->item(item_id); 39 | os 40 | << std::setw(12) << item_id 41 | << std::setw(12) << item.weight 42 | << std::setw(24) << item.profit 43 | << std::setw(16) << item.efficiency 44 | << std::endl; 45 | } 46 | } 47 | } 48 | 49 | void Instance::write( 50 | std::string instance_path) const 51 | { 52 | std::ofstream file(instance_path); 53 | if (!file.good()) { 54 | throw std::runtime_error( 55 | "Unable to open file \"" + instance_path + "\"."); 56 | } 57 | 58 | file << number_of_items() << " " << capacity() << std::endl; 59 | for (ItemId item_id = 0; item_id < number_of_items(); ++item_id) { 60 | file << item(item_id).profit << " " << item(item_id).weight << std::endl; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/knapsack/instance_builder.cpp: -------------------------------------------------------------------------------- 1 | #include "knapsacksolver/knapsack/instance_builder.hpp" 2 | 3 | #include "optimizationtools/utils/utils.hpp" 4 | 5 | #include 6 | 7 | using namespace knapsacksolver::knapsack; 8 | 9 | void InstanceBuilder::add_item( 10 | Profit profit, 11 | Weight weight) 12 | { 13 | Item item; 14 | item.profit = profit; 15 | item.weight = weight; 16 | instance_.items_.push_back(item); 17 | }; 18 | 19 | void InstanceBuilder::read( 20 | const std::string& instance_path, 21 | const std::string& format) 22 | { 23 | std::ifstream file(instance_path); 24 | if (!file.good()) { 25 | throw std::runtime_error( 26 | "Unable to open file \"" + instance_path + "\"."); 27 | } 28 | 29 | if (format == "standard" || format == "") { 30 | read_standard(file); 31 | } else if (format == "pisinger") { 32 | read_pisinger(file); 33 | } else if (format == "jooken") { 34 | read_jooken(file); 35 | } else if (format == "subset_sum_standard") { 36 | read_subset_sum_standard(file); 37 | } else { 38 | throw std::invalid_argument( 39 | "Unknown instance format \"" + format + "\"."); 40 | } 41 | 42 | file.close(); 43 | } 44 | 45 | void InstanceBuilder::read_standard(std::ifstream& file) 46 | { 47 | ItemId number_of_items; 48 | Weight weight; 49 | Profit profit; 50 | file >> number_of_items >> weight; 51 | set_capacity(weight); 52 | for (ItemPos item_id = 0; item_id < number_of_items; ++item_id) { 53 | file >> profit >> weight; 54 | add_item(profit, weight); 55 | } 56 | } 57 | 58 | void InstanceBuilder::read_pisinger(std::ifstream& file) 59 | { 60 | ItemId number_of_items; 61 | Weight capacity; 62 | std::string tmp; 63 | file >> tmp >> tmp >> number_of_items >> tmp >> capacity >> tmp >> tmp >> tmp >> tmp; 64 | set_capacity(capacity); 65 | 66 | getline(file, tmp); 67 | for (ItemPos item_id = 0; item_id < number_of_items; ++item_id) { 68 | getline(file, tmp); 69 | std::vector line = optimizationtools::split(tmp, ','); 70 | Profit profit = std::stol(line[1]); 71 | Weight weight = std::stol(line[2]); 72 | add_item(profit, weight); 73 | } 74 | } 75 | 76 | void InstanceBuilder::read_jooken(std::ifstream& file) 77 | { 78 | ItemId number_of_items; 79 | file >> number_of_items; 80 | 81 | ItemId tmp; 82 | Weight weight; 83 | Profit profit; 84 | for (ItemPos item_id = 0; item_id < number_of_items; ++item_id) { 85 | file >> tmp >> profit >> weight; 86 | add_item(profit, weight); 87 | } 88 | 89 | Weight capacity; 90 | file >> capacity; 91 | set_capacity(capacity); 92 | } 93 | 94 | void InstanceBuilder::read_subset_sum_standard(std::ifstream& file) 95 | { 96 | ItemId number_of_items; 97 | Weight capacity; 98 | file >> number_of_items >> capacity; 99 | 100 | set_capacity(capacity); 101 | 102 | Weight weight = -1; 103 | for (ItemId item_id = 0; item_id < number_of_items; ++item_id) { 104 | file >> weight; 105 | add_item(weight, weight); 106 | } 107 | } 108 | 109 | Instance InstanceBuilder::build() 110 | { 111 | // Check knapsack capacity. 112 | if (instance_.capacity() < 0) { 113 | throw std::invalid_argument( 114 | "The knapsack capacity must be positive."); 115 | } 116 | 117 | // Check that profit are positive and weights are positive and smaller than 118 | // the capacity. 119 | for (ItemId item_id = 0; 120 | item_id < instance_.number_of_items(); 121 | ++item_id) { 122 | const Item& item = instance_.items_[item_id]; 123 | if (item.profit <= 0) { 124 | throw std::invalid_argument( 125 | "Items must have strictly positive profits."); 126 | } 127 | if (item.weight <= 0) { 128 | throw std::invalid_argument( 129 | "Items must have strictly positive weights."); 130 | } 131 | if (item.weight > instance_.capacity()) { 132 | throw std::invalid_argument( 133 | "The weight of an item must be smaller than the knapsack capacity.."); 134 | } 135 | } 136 | 137 | // Compute total item profit and weight. 138 | for (ItemId item_id = 0; 139 | item_id < instance_.number_of_items(); 140 | ++item_id) { 141 | const Item& item = instance_.items_[item_id]; 142 | instance_.highest_item_profit_ = std::max(instance_.highest_item_profit_, item.profit); 143 | instance_.highest_item_weight_ = std::max(instance_.highest_item_weight_, item.weight); 144 | instance_.total_item_profit_ += item.profit; 145 | instance_.total_item_weight_ += item.weight; 146 | } 147 | 148 | // Compute item efficiencies. 149 | for (ItemId item_id = 0; 150 | item_id < instance_.number_of_items(); 151 | ++item_id) { 152 | Item& item = instance_.items_[item_id]; 153 | item.efficiency = (double)item.profit / item.weight; 154 | } 155 | 156 | // Compute highest efficiency item. 157 | instance_.highest_efficiency_item_id_ = -1; 158 | for (ItemId item_id = 0; 159 | item_id < instance_.number_of_items(); 160 | ++item_id) { 161 | const Item& item = instance_.item(item_id); 162 | if (instance_.highest_efficiency_item_id_ == -1 163 | || item.efficiency 164 | > instance_.item(instance_.highest_efficiency_item_id_).efficiency) { 165 | instance_.highest_efficiency_item_id_ = item_id; 166 | } 167 | } 168 | 169 | return std::move(instance_); 170 | } 171 | 172 | void InstanceFromFloatProfitsBuilder::add_item( 173 | double profit, 174 | Weight weight) 175 | { 176 | if (profit != profit) { 177 | throw std::invalid_argument( 178 | "Item profits must not be NaN."); 179 | } 180 | 181 | profits_double_.push_back(profit); 182 | Item item; 183 | item.profit = -1; 184 | item.weight = weight; 185 | instance_.items_.push_back(item); 186 | }; 187 | 188 | Instance InstanceFromFloatProfitsBuilder::build() 189 | { 190 | if (instance_.number_of_items() == 0) 191 | return std::move(instance_); 192 | 193 | // Check overflow because of total profit. 194 | // multiplier * total_profit_double < INT_MAX 195 | // multiplier < INT_MAX / total_profit_double 196 | double total_item_profit = 0; 197 | for (double profit: profits_double_) 198 | total_item_profit += profit; 199 | double highest_possible_multiplier = (double)std::numeric_limits::max() / total_item_profit; 200 | 201 | // Check highest possible profit to avoid overflows when sorting. 202 | double highest_item_profit = 0; 203 | for (double profit: profits_double_) 204 | highest_item_profit = std::max(highest_item_profit, profit); 205 | Weight highest_item_weight = 0; 206 | for (ItemId item_id = 0; 207 | item_id < instance_.number_of_items(); 208 | ++item_id) { 209 | const Item& item = instance_.item(item_id); 210 | highest_item_weight = std::max(highest_item_weight, item.weight); 211 | } 212 | Profit highest_possible_item_profit = std::numeric_limits::max() / highest_item_weight; 213 | highest_possible_multiplier = std::min( 214 | highest_possible_multiplier, 215 | highest_possible_item_profit / highest_item_profit); 216 | 217 | // Check no overflow because of the bounds. 218 | // We want to ensure that: 219 | // profit * capacity < INT_MAX 220 | // Since 221 | // profit = profit_double * multiplier 222 | // That is: 223 | // multiplier < INT_MAX / capacity / profit_double 224 | for (ItemId item_id = 0; 225 | item_id < instance_.number_of_items(); 226 | ++item_id) { 227 | highest_possible_multiplier = std::min( 228 | highest_possible_multiplier, 229 | (double)(std::numeric_limits::max()) 230 | / instance_.capacity_ 231 | / profits_double_[item_id]); 232 | } 233 | 234 | // Compute multiplier. 235 | double multiplier = 1; 236 | while (2 * multiplier < highest_possible_multiplier) 237 | multiplier *= 2; 238 | while (multiplier > highest_possible_multiplier) 239 | multiplier /= 2; 240 | 241 | // Compute integer profits. 242 | for (ItemId item_id = 0; 243 | item_id < instance_.number_of_items(); 244 | ++item_id) { 245 | Profit profit = std::round(profits_double_[item_id] * multiplier); 246 | if (profit == 0) 247 | profit = 1; 248 | instance_.items_[item_id].profit = profit; 249 | } 250 | 251 | // Check knapsack capacity. 252 | if (instance_.capacity() < 0) { 253 | throw std::invalid_argument( 254 | "The knapsack capacity must be positive."); 255 | } 256 | 257 | // Check that profit are positive and weights are positive and smaller than 258 | // the capacity. 259 | for (ItemId item_id = 0; 260 | item_id < instance_.number_of_items(); 261 | ++item_id) { 262 | const Item& item = instance_.items_[item_id]; 263 | if (item.profit <= 0) { 264 | throw std::invalid_argument( 265 | "Items must have strictly positive profits."); 266 | } 267 | if (item.weight <= 0) { 268 | throw std::invalid_argument( 269 | "Items must have strictly positive weights."); 270 | } 271 | if (item.weight > instance_.capacity()) { 272 | throw std::invalid_argument( 273 | "The weight of an item must be smaller than the knapsack capacity.."); 274 | } 275 | } 276 | 277 | // Compute total item profit and weight. 278 | for (ItemId item_id = 0; 279 | item_id < instance_.number_of_items(); 280 | ++item_id) { 281 | const Item& item = instance_.items_[item_id]; 282 | instance_.highest_item_profit_ = std::max(instance_.highest_item_profit_, item.profit); 283 | instance_.highest_item_weight_ = std::max(instance_.highest_item_weight_, item.weight); 284 | instance_.total_item_profit_ += item.profit; 285 | instance_.total_item_weight_ += item.weight; 286 | } 287 | 288 | // Compute item efficiencies. 289 | for (ItemId item_id = 0; 290 | item_id < instance_.number_of_items(); 291 | ++item_id) { 292 | Item& item = instance_.items_[item_id]; 293 | item.efficiency = (double)item.profit / item.weight; 294 | } 295 | 296 | // Compute highest efficiency item. 297 | instance_.highest_efficiency_item_id_ = -1; 298 | for (ItemId item_id = 0; 299 | item_id < instance_.number_of_items(); 300 | ++item_id) { 301 | const Item& item = instance_.item(item_id); 302 | if (instance_.highest_efficiency_item_id_ == -1 303 | || item.efficiency 304 | > instance_.item(instance_.highest_efficiency_item_id_).efficiency) { 305 | instance_.highest_efficiency_item_id_ = item_id; 306 | } 307 | } 308 | 309 | return std::move(instance_); 310 | } 311 | -------------------------------------------------------------------------------- /src/knapsack/main.cpp: -------------------------------------------------------------------------------- 1 | #include "knapsacksolver/knapsack/instance_builder.hpp" 2 | 3 | #include "knapsacksolver/knapsack/upper_bound.hpp" 4 | #include "knapsacksolver/knapsack/algorithms/upper_bound_dantzig.hpp" 5 | #include "knapsacksolver/knapsack/algorithms/greedy.hpp" 6 | #include "knapsacksolver/knapsack/algorithms/dynamic_programming_bellman.hpp" 7 | #include "knapsacksolver/knapsack/algorithms/dynamic_programming_primal_dual.hpp" 8 | 9 | #include 10 | 11 | using namespace knapsacksolver::knapsack; 12 | 13 | namespace po = boost::program_options; 14 | 15 | void read_args( 16 | Parameters& parameters, 17 | const po::variables_map& vm) 18 | { 19 | parameters.timer.set_sigint_handler(); 20 | parameters.messages_to_stdout = true; 21 | if (vm.count("time-limit")) 22 | parameters.timer.set_time_limit(vm["time-limit"].as()); 23 | if (vm.count("verbosity-level")) 24 | parameters.verbosity_level = vm["verbosity-level"].as(); 25 | if (vm.count("log")) 26 | parameters.log_path = vm["log"].as(); 27 | parameters.log_to_stderr = vm.count("log-to-stderr"); 28 | parameters.json_output = (vm.count("output")); 29 | bool only_write_at_the_end = vm.count("only-write-at-the-end"); 30 | if (!only_write_at_the_end) { 31 | std::string certificate_path = vm["certificate"].as(); 32 | std::string json_output_path = vm["output"].as(); 33 | parameters.new_solution_callback = [ 34 | json_output_path, 35 | certificate_path]( 36 | const Output& output) 37 | { 38 | output.write_json_output(json_output_path); 39 | output.solution.write(certificate_path); 40 | }; 41 | } 42 | } 43 | 44 | Output run( 45 | const Instance& instance, 46 | const po::variables_map& vm) 47 | { 48 | std::mt19937_64 generator(vm["seed"].as()); 49 | Solution solution(instance, vm["initial-solution"].as()); 50 | 51 | // Run algorithm. 52 | std::string algorithm = "dynamic-programming-primal-dual"; 53 | if (vm.count("algorithm")) 54 | algorithm = vm["algorithm"].as(); 55 | 56 | if (algorithm == "upper-bound-dantzig") { 57 | UpperBoundDantzigParameters parameters; 58 | read_args(parameters, vm); 59 | return upper_bound_dantzig(instance, parameters); 60 | 61 | } else if (algorithm == "greedy") { 62 | GreedyParameters parameters; 63 | read_args(parameters, vm); 64 | return greedy(instance, parameters); 65 | 66 | } else if (algorithm == "dynamic-programming-bellman-rec") { 67 | Parameters parameters; 68 | read_args(parameters, vm); 69 | return dynamic_programming_bellman_rec(instance, parameters); 70 | } else if (algorithm == "dynamic-programming-bellman-array") { 71 | Parameters parameters; 72 | read_args(parameters, vm); 73 | return dynamic_programming_bellman_array(instance, parameters); 74 | } else if (algorithm == "dynamic-programming-bellman-array-parallel") { 75 | Parameters parameters; 76 | read_args(parameters, vm); 77 | return dynamic_programming_bellman_array_parallel(instance, parameters); 78 | } else if (algorithm == "dynamic-programming-bellman-array-all") { 79 | Parameters parameters; 80 | read_args(parameters, vm); 81 | return dynamic_programming_bellman_array_all(instance, parameters); 82 | } else if (algorithm == "dynamic-programming-bellman-array-one") { 83 | Parameters parameters; 84 | read_args(parameters, vm); 85 | return dynamic_programming_bellman_array_one(instance, parameters); 86 | } else if (algorithm == "dynamic-programming-bellman-array-part") { 87 | DynamicProgrammingBellmanArrayPartParameters parameters; 88 | read_args(parameters, vm); 89 | if (vm.count("partial-solution-size")) 90 | parameters.partial_solution_size = vm["partial-solution-size"].as(); 91 | return dynamic_programming_bellman_array_part(instance, parameters); 92 | } else if (algorithm == "dynamic-programming-bellman-array-rec") { 93 | Parameters parameters; 94 | read_args(parameters, vm); 95 | return dynamic_programming_bellman_array_rec(instance, parameters); 96 | } else if (algorithm == "dynamic-programming-bellman-list") { 97 | DynamicProgrammingBellmanListParameters parameters; 98 | read_args(parameters, vm); 99 | if (vm.count("sort")) 100 | parameters.sort = vm["sort"].as(); 101 | return dynamic_programming_bellman_list(instance, parameters); 102 | 103 | } else if (algorithm == "dynamic-programming-primal-dual" 104 | || algorithm == "minknap") { 105 | DynamicProgrammingPrimalDualParameters parameters; 106 | read_args(parameters, vm); 107 | if (vm.count("partial-solution-size")) 108 | parameters.partial_solution_size = vm["partial-solution-size"].as(); 109 | if (vm.count("pairing")) 110 | parameters.pairing = vm["pairing"].as(); 111 | return dynamic_programming_primal_dual(instance, parameters); 112 | 113 | } else { 114 | throw std::invalid_argument( 115 | "Unknown algorithm \"" + algorithm + "\"."); 116 | } 117 | } 118 | 119 | int main(int argc, char *argv[]) 120 | { 121 | // Parse program options 122 | po::options_description desc("Allowed options"); 123 | desc.add_options() 124 | ("help,h", "produce help message") 125 | ("algorithm,a", po::value(), "set algorithm") 126 | ("input,i", po::value()->required(), "set input file (required)") 127 | ("format,f", po::value()->default_value(""), "set input file format (default: standard)") 128 | ("output,o", po::value()->default_value(""), "set JSON output file") 129 | ("initial-solution,", po::value()->default_value(""), "") 130 | ("certificate,c", po::value()->default_value(""), "set certificate file") 131 | ("seed,s", po::value()->default_value(0), "set seed") 132 | ("time-limit,t", po::value(), "set time limit in seconds") 133 | ("verbosity-level,v", po::value(), "set verbosity level") 134 | ("only-write-at-the-end,e", "only write output and certificate files at the end") 135 | ("log,l", po::value(), "set log file") 136 | ("log-to-stderr", "write log to stderr") 137 | 138 | ("sort,", po::value(), "set sort") 139 | ("partial-solution-size,", po::value(), "set partial solution size") 140 | ("pairing,", po::value(), "set pairing") 141 | ; 142 | po::variables_map vm; 143 | po::store(po::parse_command_line(argc, argv, desc), vm); 144 | if (vm.count("help")) { 145 | std::cout << desc << std::endl;; 146 | return 1; 147 | } 148 | try { 149 | po::notify(vm); 150 | } catch (const po::required_option& e) { 151 | std::cout << desc << std::endl;; 152 | return 1; 153 | } 154 | 155 | // Build instance. 156 | InstanceBuilder instance_builder; 157 | instance_builder.read( 158 | vm["input"].as(), 159 | vm["format"].as()); 160 | const Instance instance = instance_builder.build(); 161 | 162 | // Run. 163 | Output output = run(instance, vm); 164 | 165 | // Write outputs. 166 | std::string certificate_path = vm["certificate"].as(); 167 | std::string json_output_path = vm["output"].as(); 168 | output.write_json_output(json_output_path); 169 | output.solution.write(certificate_path); 170 | 171 | return 0; 172 | } 173 | -------------------------------------------------------------------------------- /src/knapsack/solution.cpp: -------------------------------------------------------------------------------- 1 | #include "knapsacksolver/knapsack/solution.hpp" 2 | 3 | using namespace knapsacksolver::knapsack; 4 | 5 | Solution::Solution(const Instance& instance): 6 | instance_(&instance), 7 | contains_(instance.number_of_items(), 0) 8 | { } 9 | 10 | Solution::Solution( 11 | const Instance& instance, 12 | std::string certificate_path, 13 | std::string certificate_format): 14 | Solution(instance) 15 | { 16 | if (certificate_path.empty()) 17 | return; 18 | std::ifstream file(certificate_path); 19 | if (!file.good()) { 20 | throw std::runtime_error( 21 | "Unable to open file \"" + certificate_path + "\"."); 22 | } 23 | 24 | if (certificate_format == "standard") { 25 | ItemPos number_of_items; 26 | ItemPos item_id; 27 | file >> number_of_items; 28 | for (ItemPos pos = 0; pos < number_of_items; ++pos) { 29 | file >> item_id; 30 | add(item_id); 31 | } 32 | } else if (certificate_format == "pisinger") { 33 | std::string tmp; 34 | file >> tmp >> tmp >> tmp >> tmp >> tmp >> tmp >> tmp >> tmp >> tmp; 35 | getline(file, tmp); 36 | for (ItemPos item_id = 0; 37 | item_id < instance.number_of_items(); 38 | ++item_id) { 39 | getline(file, tmp); 40 | std::vector line = optimizationtools::split(tmp, ','); 41 | int contains = std::stol(line[3]); 42 | if (contains) 43 | add(item_id); 44 | } 45 | } else { 46 | throw std::invalid_argument( 47 | "Unknown certificate format \"" + certificate_format + "\"."); 48 | } 49 | } 50 | 51 | void Solution::add(ItemId item_id) 52 | { 53 | contains_[item_id] = 1; 54 | number_of_items_++; 55 | weight_ += instance().item(item_id).weight; 56 | profit_ += instance().item(item_id).profit; 57 | } 58 | 59 | void Solution::remove(ItemId item_id) 60 | { 61 | contains_[item_id] = 0; 62 | number_of_items_--; 63 | weight_ -= instance().item(item_id).weight; 64 | profit_ -= instance().item(item_id).profit; 65 | } 66 | 67 | void Solution::fill() 68 | { 69 | for (ItemId item_id = 0; 70 | item_id < instance().number_of_items(); 71 | ++item_id) { 72 | add(item_id); 73 | } 74 | } 75 | 76 | void Solution::write(std::string certificate_path) const 77 | { 78 | if (certificate_path.empty()) 79 | return; 80 | std::ofstream file(certificate_path); 81 | if (!file.good()) { 82 | throw std::runtime_error( 83 | "Unable to open file \"" + certificate_path + "\"."); 84 | } 85 | 86 | file << number_of_items() << std::endl; 87 | for (ItemId item_id = 0; item_id < instance().number_of_items(); ++item_id) 88 | if (contains(item_id)) 89 | file << item_id << std::endl; 90 | } 91 | 92 | void Solution::format( 93 | std::ostream& os, 94 | int verbosity_level) const 95 | { 96 | if (verbosity_level >= 1) { 97 | os 98 | << "Number of items: " << optimizationtools::Ratio(number_of_items(), instance().number_of_items()) << std::endl 99 | << "Weight: " << optimizationtools::Ratio(weight(), instance().capacity()) << std::endl 100 | << "Profit: " << profit() << std::endl 101 | << "Feasible: " << feasible() << std::endl 102 | ; 103 | } 104 | 105 | if (verbosity_level >= 2) { 106 | os << std::right << std::endl 107 | << std::setw(12) << "Item" 108 | << std::endl 109 | << std::setw(12) << "----" 110 | << std::endl; 111 | for (ItemId item_id = 0; 112 | item_id < instance().number_of_items(); 113 | ++item_id) { 114 | if (!contains(item_id)) 115 | continue; 116 | os 117 | << std::setw(12) << item_id 118 | << std::endl; 119 | } 120 | } 121 | } 122 | 123 | nlohmann::json Solution::to_json() const 124 | { 125 | return nlohmann::json { 126 | {"NumberOfItems", number_of_items()}, 127 | {"Feasible", feasible()}, 128 | {"Weight", weight()}, 129 | {"Profit", profit()}, 130 | }; 131 | } 132 | -------------------------------------------------------------------------------- /src/knapsack/tests.cpp: -------------------------------------------------------------------------------- 1 | #include "knapsacksolver/knapsack/tests.hpp" 2 | 3 | #include "knapsacksolver/knapsack/instance_builder.hpp" 4 | 5 | #include 6 | 7 | using namespace knapsacksolver::knapsack; 8 | 9 | namespace fs = boost::filesystem; 10 | 11 | std::string knapsacksolver::knapsack::get_path( 12 | const std::vector& path) 13 | { 14 | if (path.empty()) 15 | return ""; 16 | fs::path p(path[0]); 17 | for (size_t i = 1; i < path.size(); ++i) 18 | p /= path[i]; 19 | return p.string(); 20 | } 21 | 22 | std::vector knapsacksolver::knapsack::get_test_instance_paths() 23 | { 24 | return { 25 | { 26 | get_path({"tests", "instance_0_item.txt"}), "standard", 27 | get_path({"tests", "instance_0_item.txt"}), "standard" 28 | }, { 29 | get_path({"tests", "instance_1_item.txt"}), "standard", 30 | get_path({"tests", "instance_1_item.sol.txt"}), "standard" 31 | }, { 32 | get_path({"tests", "instance_1_item_tight_capacity.txt"}), "standard", 33 | get_path({"tests", "instance_1_item_tight_capacity.sol.txt"}), "standard" 34 | }, { 35 | get_path({"tests", "instance_2_items.txt"}), "standard", 36 | get_path({"tests", "instance_2_items.sol.txt"}), "standard" 37 | }, { 38 | get_path({"tests", "instance_2_fitting_items.txt"}), "standard", 39 | get_path({"tests", "instance_2_fitting_items.sol.txt"}), "standard" 40 | }, { 41 | get_path({"tests", "instance_3_items_1.txt"}), "standard", 42 | get_path({"tests", "instance_3_items_1.sol.txt"}), "standard" 43 | }, { 44 | get_path({"tests", "instance_3_items_2.txt"}), "standard", 45 | get_path({"tests", "instance_3_items_2.sol.txt"}), "standard" 46 | }, { 47 | get_path({"tests", "instance_3_items_3.txt"}), "standard", 48 | get_path({"tests", "instance_3_items_3.sol.txt"}), "standard"}, 49 | { 50 | get_path({"tests", "instance_4_items.txt"}), "standard", 51 | get_path({"tests", "instance_4_items.sol.txt"}), "standard" 52 | }, { 53 | get_path({"tests", "instance_5_items.txt"}), "standard", 54 | get_path({"tests", "instance_5_items.sol.txt"}), "standard" 55 | }, { 56 | get_path({"tests", "instance_7_items.txt"}), "standard", 57 | get_path({"tests", "instance_7_items.sol.txt"}), "standard" 58 | }, { 59 | get_path({"tests", "instance_9_items.txt"}), "standard", 60 | get_path({"tests", "instance_9_items.sol.txt"}), "standard" 61 | }, { 62 | get_path({"tests", "instance_debug.txt"}), "standard", 63 | get_path({"tests", "instance_debug.sol.txt"}), "standard" 64 | }, 65 | }; 66 | } 67 | 68 | std::vector knapsacksolver::knapsack::get_pisinger_instance_paths( 69 | const std::string& s1, 70 | const std::string& s2) 71 | { 72 | std::vector instance_paths; 73 | for (int i = 1; i <= 100; ++i) { 74 | instance_paths.push_back({ 75 | get_path({s1, s2, s2 + "_" + std::to_string(i) + ".csv"}), "pisinger", 76 | get_path({s1, s2, s2 + "_" + std::to_string(i) + ".csv"}), "pisinger"}); 77 | } 78 | return instance_paths; 79 | } 80 | 81 | std::vector knapsacksolver::knapsack::get_test_params( 82 | const std::vector& algorithms, 83 | const std::vector>& instance_paths) 84 | { 85 | std::vector res; 86 | for (const Algorithm& algorithm: algorithms) { 87 | for (const auto& v: instance_paths) { 88 | for (const TestInstancePath& files: v) { 89 | TestParams test_params; 90 | test_params.algorithm = algorithm; 91 | test_params.files = files; 92 | res.push_back(test_params); 93 | } 94 | } 95 | } 96 | return res; 97 | } 98 | 99 | const Instance knapsacksolver::knapsack::get_instance( 100 | const TestInstancePath& files) 101 | { 102 | InstanceBuilder instance_builder; 103 | std::string instance_path = get_path({ 104 | std::getenv("KNAPSACK_DATA"), 105 | files.instance_path}); 106 | std::cout << "Instance path: " << instance_path << std::endl; 107 | instance_builder.read( 108 | instance_path, 109 | files.instance_format); 110 | return instance_builder.build(); 111 | } 112 | 113 | const Solution knapsacksolver::knapsack::get_solution( 114 | const Instance& instance, 115 | const TestInstancePath& files) 116 | { 117 | std::string certificate_path = get_path({ 118 | std::getenv("KNAPSACK_DATA"), 119 | files.certificate_path}); 120 | return Solution( 121 | instance, 122 | certificate_path, 123 | files.certificate_format); 124 | } 125 | -------------------------------------------------------------------------------- /src/knapsack/upper_bound.cpp: -------------------------------------------------------------------------------- 1 | #include "knapsacksolver/knapsack/upper_bound.hpp" 2 | 3 | #include "knapsacksolver/knapsack/algorithm_formatter.hpp" 4 | 5 | using namespace knapsacksolver::knapsack; 6 | 7 | -------------------------------------------------------------------------------- /src/multiple_choice_subset_sum/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(KnapsackSolver_multiple_choice_subset_sum) 2 | target_sources(KnapsackSolver_multiple_choice_subset_sum PRIVATE 3 | instance.cpp 4 | instance_builder.cpp 5 | solution.cpp 6 | algorithm_formatter.cpp) 7 | target_include_directories(KnapsackSolver_multiple_choice_subset_sum PUBLIC 8 | ${PROJECT_SOURCE_DIR}/include) 9 | target_link_libraries(KnapsackSolver_multiple_choice_subset_sum PUBLIC 10 | OptimizationTools::utils) 11 | add_library(KnapsackSolver::multiple_choice_subset_sum ALIAS KnapsackSolver_multiple_choice_subset_sum) 12 | 13 | add_subdirectory(algorithms) 14 | 15 | add_executable(KnapsackSolver_multiple_choice_subset_sum_main) 16 | target_sources(KnapsackSolver_multiple_choice_subset_sum_main PRIVATE 17 | main.cpp) 18 | target_link_libraries(KnapsackSolver_multiple_choice_subset_sum_main PUBLIC 19 | KnapsackSolver_multiple_choice_subset_sum_dynamic_programming_bellman 20 | Boost::program_options) 21 | set_target_properties(KnapsackSolver_multiple_choice_subset_sum_main PROPERTIES OUTPUT_NAME "knapsacksolver_multiple_choice_subset_sum") 22 | install(TARGETS KnapsackSolver_multiple_choice_subset_sum_main) 23 | -------------------------------------------------------------------------------- /src/multiple_choice_subset_sum/algorithm_formatter.cpp: -------------------------------------------------------------------------------- 1 | #include "knapsacksolver/multiple_choice_subset_sum/algorithm_formatter.hpp" 2 | 3 | #include "optimizationtools/utils/utils.hpp" 4 | 5 | #include 6 | 7 | using namespace knapsacksolver::multiple_choice_subset_sum; 8 | 9 | void AlgorithmFormatter::start( 10 | const std::string& algorithm_name) 11 | { 12 | output_.json["Parameters"] = parameters_.to_json(); 13 | 14 | if (parameters_.verbosity_level == 0) 15 | return; 16 | *os_ 17 | << "====================================" << std::endl 18 | << " KnapsackSolver " << std::endl 19 | << "====================================" << std::endl 20 | << std::endl 21 | << "Problem" << std::endl 22 | << "-------" << std::endl 23 | << "Multiple-choice subset sum problem" << std::endl 24 | << std::endl 25 | << "Instance" << std::endl 26 | << "--------" << std::endl; 27 | output_.solution.instance().format(*os_, parameters_.verbosity_level); 28 | *os_ 29 | << std::endl 30 | << "Algorithm" << std::endl 31 | << "---------" << std::endl 32 | << algorithm_name << std::endl 33 | << std::endl 34 | << "Parameters" << std::endl 35 | << "----------" << std::endl; 36 | parameters_.format(*os_); 37 | } 38 | 39 | void AlgorithmFormatter::print_header() 40 | { 41 | if (parameters_.verbosity_level == 0) 42 | return; 43 | *os_ 44 | << std::right 45 | << std::endl 46 | << std::setw(12) << "Time (s)" 47 | << std::setw(6) << "Sol." 48 | << std::setw(12) << "Value" 49 | << std::setw(12) << "Bound" 50 | << std::setw(12) << "Gap" 51 | << std::setw(12) << "Gap (%)" 52 | << std::setw(32) << "Comment" 53 | << std::endl 54 | << std::setw(12) << "--------" 55 | << std::setw(6) << "----" 56 | << std::setw(12) << "-----" 57 | << std::setw(12) << "-----" 58 | << std::setw(12) << "---" 59 | << std::setw(12) << "-------" 60 | << std::setw(32) << "-------" 61 | << std::endl; 62 | print(""); 63 | } 64 | 65 | void AlgorithmFormatter::print( 66 | const std::string& s) 67 | { 68 | if (parameters_.verbosity_level == 0) 69 | return; 70 | std::streamsize precision = std::cout.precision(); 71 | *os_ 72 | << std::setw(12) << std::fixed << std::setprecision(3) << output_.time << std::defaultfloat << std::setprecision(precision) 73 | << std::setw(6) << output_.has_solution() 74 | << std::setw(12) << output_.value 75 | << std::setw(12) << output_.bound 76 | << std::setw(12) << output_.absolute_optimality_gap() 77 | << std::setw(12) << std::fixed << std::setprecision(2) << output_.relative_optimality_gap() * 100 << std::defaultfloat << std::setprecision(precision) 78 | << std::setw(32) << s << std::endl; 79 | } 80 | 81 | void AlgorithmFormatter::update_solution( 82 | const Solution& solution_new, 83 | const std::string& s) 84 | { 85 | if ((output_.has_solution() && optimizationtools::is_solution_strictly_better( 86 | objective_direction(), 87 | output_.value, 88 | solution_new.feasible(), 89 | solution_new.objective_value())) 90 | || (!output_.has_solution() && optimizationtools::is_solution_better_or_equal( 91 | objective_direction(), 92 | output_.value, 93 | solution_new.feasible(), 94 | solution_new.objective_value()))) { 95 | output_.time = parameters_.timer.elapsed_time(); 96 | output_.solution = solution_new; 97 | output_.value = output_.solution.objective_value(); 98 | print(s); 99 | output_.json["IntermediaryOutputs"].push_back(output_.to_json()); 100 | parameters_.new_solution_callback(output_); 101 | } 102 | } 103 | 104 | void AlgorithmFormatter::update_value( 105 | Weight value_new, 106 | const std::string& s) 107 | { 108 | if (optimizationtools::is_value_strictly_better( 109 | objective_direction(), 110 | output_.value, 111 | value_new)) { 112 | output_.time = parameters_.timer.elapsed_time(); 113 | output_.value = value_new; 114 | print(s); 115 | output_.json["IntermediaryOutputs"].push_back(output_.to_json()); 116 | parameters_.new_solution_callback(output_); 117 | } 118 | } 119 | 120 | void AlgorithmFormatter::update_bound( 121 | Weight bound_new, 122 | const std::string& s) 123 | { 124 | if (optimizationtools::is_bound_strictly_better( 125 | objective_direction(), 126 | output_.bound, 127 | bound_new)) { 128 | output_.time = parameters_.timer.elapsed_time(); 129 | output_.bound = bound_new; 130 | print(s); 131 | output_.json["IntermediaryOutputs"].push_back(output_.to_json()); 132 | parameters_.new_solution_callback(output_); 133 | } 134 | } 135 | 136 | void AlgorithmFormatter::end() 137 | { 138 | output_.time = parameters_.timer.elapsed_time(); 139 | output_.json["Output"] = output_.to_json(); 140 | 141 | if (parameters_.verbosity_level == 0) 142 | return; 143 | *os_ 144 | << std::endl 145 | << "Final statistics" << std::endl 146 | << "----------------" << std::endl; 147 | output_.format(*os_); 148 | *os_ 149 | << std::endl 150 | << "Solution" << std::endl 151 | << "--------" << std::endl; 152 | output_.solution.format(*os_, parameters_.verbosity_level); 153 | } 154 | -------------------------------------------------------------------------------- /src/multiple_choice_subset_sum/algorithms/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(KnapsackSolver_multiple_choice_subset_sum_dynamic_programming_bellman) 2 | target_sources(KnapsackSolver_multiple_choice_subset_sum_dynamic_programming_bellman PRIVATE 3 | dynamic_programming_bellman.cpp) 4 | target_include_directories(KnapsackSolver_multiple_choice_subset_sum_dynamic_programming_bellman PUBLIC 5 | ${PROJECT_SOURCE_DIR}/include) 6 | target_link_libraries(KnapsackSolver_multiple_choice_subset_sum_dynamic_programming_bellman PUBLIC 7 | KnapsackSolver_multiple_choice_subset_sum) 8 | add_library(KnapsackSolver::multiple_choice_subset_sum::dynamic_programming_bellman ALIAS KnapsackSolver_multiple_choice_subset_sum_dynamic_programming_bellman) 9 | -------------------------------------------------------------------------------- /src/multiple_choice_subset_sum/instance.cpp: -------------------------------------------------------------------------------- 1 | #include "knapsacksolver/multiple_choice_subset_sum/instance.hpp" 2 | 3 | #include 4 | 5 | using namespace knapsacksolver::multiple_choice_subset_sum; 6 | 7 | void Instance::format( 8 | std::ostream& os, 9 | int verbosity_level) const 10 | { 11 | if (verbosity_level >= 1) { 12 | os 13 | << "Number of groups: " << number_of_groups() << std::endl 14 | << "Number of items: " << number_of_items() << std::endl 15 | << "Capacity: " << capacity() << std::endl 16 | ; 17 | } 18 | 19 | if (verbosity_level >= 2) { 20 | os 21 | << std::endl 22 | << std::setw(12) << "Group" 23 | << std::setw(12) << "Size" 24 | << std::endl 25 | << std::setw(12) << "-----" 26 | << std::setw(12) << "----" 27 | << std::endl; 28 | for (GroupId group_id = 0; group_id < number_of_groups(); ++group_id) { 29 | os 30 | << std::setw(12) << group_id 31 | << std::setw(12) << number_of_items(group_id) 32 | << std::endl; 33 | } 34 | 35 | os 36 | << std::endl 37 | << std::setw(12) << "Item" 38 | << std::setw(12) << "Group" 39 | << std::setw(12) << "Weight" 40 | << std::endl 41 | << std::setw(12) << "----" 42 | << std::setw(12) << "-----" 43 | << std::setw(12) << "------" 44 | << std::endl; 45 | for (ItemId item_id = 0; item_id < number_of_items(); ++item_id) { 46 | const Item& item = this->item(item_id); 47 | os 48 | << std::setw(12) << item_id 49 | << std::setw(12) << item.group_id 50 | << std::setw(12) << item.weight 51 | << std::endl; 52 | } 53 | } 54 | } 55 | 56 | void Instance::write( 57 | std::string instance_path) const 58 | { 59 | std::ofstream file(instance_path); 60 | if (!file.good()) { 61 | throw std::runtime_error( 62 | "Unable to open file \"" + instance_path + "\"."); 63 | } 64 | 65 | file << number_of_groups() << " " << capacity() << std::endl; 66 | for (GroupId group_id = 0; group_id < number_of_groups(); ++group_id) { 67 | file << number_of_items(group_id); 68 | for (ItemPos item_id = 0; 69 | item_id < number_of_items(group_id); 70 | ++item_id) 71 | file << " " << item(item_id).weight; 72 | file << std::endl; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/multiple_choice_subset_sum/instance_builder.cpp: -------------------------------------------------------------------------------- 1 | #include "knapsacksolver/multiple_choice_subset_sum/instance_builder.hpp" 2 | 3 | #include 4 | 5 | using namespace knapsacksolver::multiple_choice_subset_sum; 6 | 7 | void InstanceBuilder::add_item( 8 | GroupId group_id, 9 | Weight weight) 10 | { 11 | ItemId item_id = instance_.items_.size(); 12 | Item item; 13 | item.group_id = group_id; 14 | item.weight = weight; 15 | instance_.items_.push_back(item); 16 | while ((GroupId)instance_.groups_.size() <= group_id) 17 | instance_.groups_.push_back(Group()); 18 | instance_.groups_[group_id].item_ids.push_back(item_id); 19 | } 20 | 21 | void InstanceBuilder::read( 22 | const std::string& instance_path, 23 | const std::string& format) 24 | { 25 | std::ifstream file(instance_path); 26 | if (!file.good()) { 27 | throw std::runtime_error( 28 | "Unable to open file \"" + instance_path + "\"."); 29 | } 30 | 31 | if (format == "standard" || format == "") { 32 | read_standard(file); 33 | } else { 34 | throw std::invalid_argument( 35 | "Unknown instance format \"" + format + "\"."); 36 | } 37 | 38 | file.close(); 39 | } 40 | 41 | void InstanceBuilder::read_standard(std::ifstream& file) 42 | { 43 | GroupId number_of_groups; 44 | Weight weight; 45 | ItemPos group_number_of_items; 46 | 47 | file >> number_of_groups >> weight; 48 | set_capacity(weight); 49 | for (GroupId group_id = 0; group_id < number_of_groups; ++group_id) { 50 | file >> group_number_of_items; 51 | for (ItemPos item_id = 0; 52 | item_id < group_number_of_items; 53 | ++item_id) { 54 | file >> weight; 55 | add_item(group_id, weight); 56 | } 57 | } 58 | } 59 | 60 | Instance InstanceBuilder::build() 61 | { 62 | return std::move(instance_); 63 | } 64 | -------------------------------------------------------------------------------- /src/multiple_choice_subset_sum/main.cpp: -------------------------------------------------------------------------------- 1 | #include "knapsacksolver/multiple_choice_subset_sum/instance_builder.hpp" 2 | 3 | #include "knapsacksolver/multiple_choice_subset_sum/algorithms/dynamic_programming_bellman.hpp" 4 | 5 | #include 6 | 7 | using namespace knapsacksolver::multiple_choice_subset_sum; 8 | 9 | namespace po = boost::program_options; 10 | 11 | void read_args( 12 | Parameters& parameters, 13 | const po::variables_map& vm) 14 | { 15 | parameters.timer.set_sigint_handler(); 16 | parameters.messages_to_stdout = true; 17 | if (vm.count("time-limit")) 18 | parameters.timer.set_time_limit(vm["time-limit"].as()); 19 | if (vm.count("verbosity-level")) 20 | parameters.verbosity_level = vm["verbosity-level"].as(); 21 | if (vm.count("log")) 22 | parameters.log_path = vm["log"].as(); 23 | parameters.log_to_stderr = vm.count("log-to-stderr"); 24 | bool only_write_at_the_end = vm.count("only-write-at-the-end"); 25 | if (!only_write_at_the_end) { 26 | std::string certificate_path = vm["certificate"].as(); 27 | std::string json_output_path = vm["output"].as(); 28 | parameters.new_solution_callback = [ 29 | json_output_path, 30 | certificate_path]( 31 | const Output& output) 32 | { 33 | output.write_json_output(json_output_path); 34 | output.solution.write(certificate_path); 35 | }; 36 | } 37 | } 38 | 39 | Output run( 40 | const Instance& instance, 41 | const po::variables_map& vm) 42 | { 43 | std::mt19937_64 generator(vm["seed"].as()); 44 | Solution solution = (!vm.count("initial-solution"))? 45 | Solution(instance): 46 | Solution(instance, vm["initial-solution"].as()); 47 | 48 | // Run algorithm. 49 | std::string algorithm = vm["algorithm"].as(); 50 | if (algorithm == "dynamic-programming-bellman-array") { 51 | Parameters parameters; 52 | read_args(parameters, vm); 53 | return dynamic_programming_bellman_array(instance, parameters); 54 | } else if (algorithm == "dynamic-programming-bellman-word-ram") { 55 | Parameters parameters; 56 | read_args(parameters, vm); 57 | return dynamic_programming_bellman_word_ram(instance, parameters); 58 | } else if (algorithm == "dynamic-programming-bellman-word-ram-rec") { 59 | Parameters parameters; 60 | read_args(parameters, vm); 61 | return dynamic_programming_bellman_word_ram_rec(instance, parameters); 62 | 63 | } else { 64 | throw std::invalid_argument( 65 | "Unknown algorithm \"" + algorithm + "\"."); 66 | } 67 | } 68 | 69 | int main(int argc, char *argv[]) 70 | { 71 | // Parse program options 72 | po::options_description desc("Allowed options"); 73 | desc.add_options() 74 | ("help,h", "produce help message") 75 | ("algorithm,a", po::value(), "set algorithm") 76 | ("input,i", po::value()->required(), "set input file (required)") 77 | ("format,f", po::value()->default_value(""), "set input file format (default: standard)") 78 | ("output,o", po::value()->default_value(""), "set JSON output file") 79 | ("certificate,c", po::value()->default_value(""), "set certificate file") 80 | ("seed,s", po::value()->default_value(0), "set seed") 81 | ("time-limit,t", po::value(), "set time limit in seconds") 82 | ("verbosity-level,v", po::value(), "set verbosity level") 83 | ("only-write-at-the-end,e", "only write output and certificate files at the end") 84 | ("log,l", po::value(), "set log file") 85 | ("log-to-stderr", "write log to stderr") 86 | ; 87 | po::variables_map vm; 88 | po::store(po::parse_command_line(argc, argv, desc), vm); 89 | if (vm.count("help")) { 90 | std::cout << desc << std::endl;; 91 | return 1; 92 | } 93 | try { 94 | po::notify(vm); 95 | } catch (const po::required_option& e) { 96 | std::cout << desc << std::endl;; 97 | return 1; 98 | } 99 | 100 | // Build instance. 101 | InstanceBuilder instance_builder; 102 | instance_builder.read( 103 | vm["input"].as(), 104 | vm["format"].as()); 105 | const Instance instance = instance_builder.build(); 106 | 107 | // Run. 108 | Output output = run(instance, vm); 109 | 110 | // Write outputs. 111 | std::string certificate_path = vm["certificate"].as(); 112 | std::string json_output_path = vm["output"].as(); 113 | output.write_json_output(json_output_path); 114 | output.solution.write(certificate_path); 115 | 116 | return 0; 117 | } 118 | -------------------------------------------------------------------------------- /src/multiple_choice_subset_sum/solution.cpp: -------------------------------------------------------------------------------- 1 | #include "knapsacksolver/multiple_choice_subset_sum/solution.hpp" 2 | 3 | #include "optimizationtools/utils/utils.hpp" 4 | 5 | using namespace knapsacksolver::multiple_choice_subset_sum; 6 | 7 | Solution::Solution(const Instance& instance): 8 | instance_(&instance), 9 | contains_item_(instance.number_of_items(), 0), 10 | contains_group_(instance.number_of_groups(), 0) 11 | { } 12 | 13 | Solution::Solution( 14 | const Instance& instance, 15 | std::string certificate_path): 16 | Solution(instance) 17 | { 18 | if (certificate_path.empty()) 19 | return; 20 | std::ifstream file(certificate_path); 21 | if (!file.good()) { 22 | throw std::runtime_error( 23 | "Unable to open file \"" + certificate_path + "\"."); 24 | } 25 | 26 | ItemPos number_of_items; 27 | ItemPos item_id; 28 | file >> number_of_items; 29 | for (ItemPos pos = 0; pos < number_of_items; ++pos) { 30 | file >> item_id; 31 | add(item_id); 32 | } 33 | } 34 | 35 | void Solution::add(ItemId item_id) 36 | { 37 | const Item& item = instance().item(item_id); 38 | contains_item_[item_id] = 1; 39 | contains_group_[item.group_id] = 1; 40 | number_of_items_++; 41 | weight_ += item.weight; 42 | } 43 | 44 | void Solution::write(std::string certificate_path) const 45 | { 46 | if (certificate_path.empty()) 47 | return; 48 | std::ofstream file(certificate_path); 49 | if (!file.good()) { 50 | throw std::runtime_error( 51 | "Unable to open file \"" + certificate_path + "\"."); 52 | } 53 | 54 | file << number_of_items() << std::endl; 55 | for (ItemId item_id = 0; item_id < instance().number_of_items(); ++item_id) 56 | if (contains_item(item_id)) 57 | file << item_id << std::endl; 58 | } 59 | 60 | void Solution::format( 61 | std::ostream& os, 62 | int verbosity_level) const 63 | { 64 | if (verbosity_level >= 1) { 65 | os 66 | << "Number of items: " << optimizationtools::Ratio(number_of_items(), instance().number_of_items()) << std::endl 67 | << "Weight: " << optimizationtools::Ratio(weight(), instance().capacity()) << std::endl 68 | << "Feasible: " << feasible() << std::endl 69 | ; 70 | } 71 | 72 | if (verbosity_level >= 2) { 73 | os << std::endl 74 | << std::setw(12) << "Item" 75 | << std::endl 76 | << std::setw(12) << "----" 77 | << std::endl; 78 | for (ItemId item_id = 0; 79 | item_id < number_of_items(); 80 | ++item_id) { 81 | os 82 | << std::setw(12) << item_id 83 | << std::endl; 84 | } 85 | } 86 | } 87 | 88 | nlohmann::json Solution::to_json() const 89 | { 90 | return nlohmann::json { 91 | {"NumberOfItems", number_of_items()}, 92 | {"Feasible", feasible()}, 93 | {"Weight", weight()}, 94 | }; 95 | } 96 | -------------------------------------------------------------------------------- /src/subset_sum/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(KnapsackSolver_subset_sum) 2 | target_sources(KnapsackSolver_subset_sum PRIVATE 3 | instance.cpp 4 | instance_builder.cpp 5 | solution.cpp 6 | algorithm_formatter.cpp) 7 | target_include_directories(KnapsackSolver_subset_sum PUBLIC 8 | ${PROJECT_SOURCE_DIR}/include) 9 | target_link_libraries(KnapsackSolver_subset_sum PUBLIC 10 | OptimizationTools::utils) 11 | add_library(KnapsackSolver::subset_sum ALIAS KnapsackSolver_subset_sum) 12 | 13 | add_subdirectory(algorithms) 14 | 15 | add_executable(KnapsackSolver_subset_sum_main) 16 | target_sources(KnapsackSolver_subset_sum_main PRIVATE 17 | main.cpp) 18 | target_link_libraries(KnapsackSolver_subset_sum_main PUBLIC 19 | KnapsackSolver_subset_sum_dynamic_programming_bellman 20 | KnapsackSolver_subset_sum_dynamic_programming_balancing 21 | KnapsackSolver_subset_sum_dynamic_programming_primal_dual 22 | Boost::program_options) 23 | set_target_properties(KnapsackSolver_subset_sum_main PROPERTIES OUTPUT_NAME "knapsacksolver_subset_sum") 24 | install(TARGETS KnapsackSolver_subset_sum_main) 25 | 26 | add_library(KnapsackSolver_subset_sum_generator) 27 | target_sources(KnapsackSolver_subset_sum_generator PRIVATE 28 | generator.cpp) 29 | target_link_libraries(KnapsackSolver_subset_sum_generator PUBLIC 30 | KnapsackSolver_subset_sum) 31 | add_library(KnapsackSolver::subset_sum::generator ALIAS KnapsackSolver_subset_sum_generator) 32 | 33 | add_library(KnapsackSolver_subset_sum_tests) 34 | target_sources(KnapsackSolver_subset_sum_tests PRIVATE 35 | tests.cpp) 36 | target_link_libraries(KnapsackSolver_subset_sum_tests PUBLIC 37 | KnapsackSolver_subset_sum 38 | Boost::filesystem 39 | GTest::gtest_main) 40 | add_library(KnapsackSolver::subset_sum::tests ALIAS KnapsackSolver_subset_sum_tests) 41 | -------------------------------------------------------------------------------- /src/subset_sum/algorithm_formatter.cpp: -------------------------------------------------------------------------------- 1 | #include "knapsacksolver/subset_sum/algorithm_formatter.hpp" 2 | 3 | #include "optimizationtools/utils/utils.hpp" 4 | 5 | #include 6 | 7 | using namespace knapsacksolver::subset_sum; 8 | 9 | void AlgorithmFormatter::start( 10 | const std::string& algorithm_name) 11 | { 12 | output_.json["Parameters"] = parameters_.to_json(); 13 | 14 | if (parameters_.verbosity_level == 0) 15 | return; 16 | *os_ 17 | << "====================================" << std::endl 18 | << " KnapsackSolver " << std::endl 19 | << "====================================" << std::endl 20 | << std::endl 21 | << "Problem" << std::endl 22 | << "-------" << std::endl 23 | << "Subset sum problem" << std::endl 24 | << std::endl 25 | << "Instance" << std::endl 26 | << "--------" << std::endl; 27 | output_.solution.instance().format(*os_, parameters_.verbosity_level); 28 | *os_ 29 | << std::endl 30 | << "Algorithm" << std::endl 31 | << "---------" << std::endl 32 | << algorithm_name << std::endl 33 | << std::endl 34 | << "Parameters" << std::endl 35 | << "----------" << std::endl; 36 | parameters_.format(*os_); 37 | } 38 | 39 | void AlgorithmFormatter::print_header() 40 | { 41 | if (parameters_.verbosity_level == 0) 42 | return; 43 | *os_ 44 | << std::right 45 | << std::endl 46 | << std::setw(12) << "Time (s)" 47 | << std::setw(6) << "Sol." 48 | << std::setw(12) << "Value" 49 | << std::setw(12) << "Bound" 50 | << std::setw(12) << "Gap" 51 | << std::setw(12) << "Gap (%)" 52 | << std::setw(32) << "Comment" 53 | << std::endl 54 | << std::setw(12) << "--------" 55 | << std::setw(6) << "----" 56 | << std::setw(12) << "-----" 57 | << std::setw(12) << "-----" 58 | << std::setw(12) << "---" 59 | << std::setw(12) << "-------" 60 | << std::setw(32) << "-------" 61 | << std::endl; 62 | print(std::string("")); 63 | } 64 | 65 | void AlgorithmFormatter::print( 66 | const std::string& s) 67 | { 68 | if (parameters_.verbosity_level == 0) 69 | return; 70 | std::streamsize precision = std::cout.precision(); 71 | *os_ 72 | << std::setw(12) << std::fixed << std::setprecision(3) << output_.time << std::defaultfloat << std::setprecision(precision) 73 | << std::setw(6) << output_.has_solution() 74 | << std::setw(12) << output_.value 75 | << std::setw(12) << output_.bound 76 | << std::setw(12) << output_.absolute_optimality_gap() 77 | << std::setw(12) << std::fixed << std::setprecision(2) << output_.relative_optimality_gap() * 100 << std::defaultfloat << std::setprecision(precision) 78 | << std::setw(32) << s << std::endl; 79 | } 80 | 81 | void AlgorithmFormatter::update_solution( 82 | const Solution& solution_new, 83 | const std::string& s) 84 | { 85 | if ((output_.has_solution() && optimizationtools::is_solution_strictly_better( 86 | objective_direction(), 87 | output_.value, 88 | solution_new.feasible(), 89 | solution_new.objective_value())) 90 | || (!output_.has_solution() && optimizationtools::is_solution_better_or_equal( 91 | objective_direction(), 92 | output_.value, 93 | solution_new.feasible(), 94 | solution_new.objective_value()))) { 95 | output_.time = parameters_.timer.elapsed_time(); 96 | output_.solution = solution_new; 97 | output_.value = output_.solution.objective_value(); 98 | print(s); 99 | output_.json["IntermediaryOutputs"].push_back(output_.to_json()); 100 | parameters_.new_solution_callback(output_); 101 | } 102 | } 103 | 104 | void AlgorithmFormatter::update_value( 105 | Weight value_new, 106 | const std::string& s) 107 | { 108 | if (optimizationtools::is_value_strictly_better( 109 | objective_direction(), 110 | output_.value, 111 | value_new)) { 112 | output_.time = parameters_.timer.elapsed_time(); 113 | output_.value = value_new; 114 | print(s); 115 | output_.json["IntermediaryOutputs"].push_back(output_.to_json()); 116 | parameters_.new_solution_callback(output_); 117 | } 118 | } 119 | 120 | void AlgorithmFormatter::update_bound( 121 | Weight bound_new, 122 | const std::string& s) 123 | { 124 | if (optimizationtools::is_bound_strictly_better( 125 | objective_direction(), 126 | output_.bound, 127 | bound_new)) { 128 | output_.time = parameters_.timer.elapsed_time(); 129 | output_.bound = bound_new; 130 | print(s); 131 | output_.json["IntermediaryOutputs"].push_back(output_.to_json()); 132 | parameters_.new_solution_callback(output_); 133 | } 134 | } 135 | 136 | void AlgorithmFormatter::end() 137 | { 138 | output_.time = parameters_.timer.elapsed_time(); 139 | output_.json["Output"] = output_.to_json(); 140 | 141 | if (parameters_.verbosity_level == 0) 142 | return; 143 | *os_ 144 | << std::endl 145 | << "Final statistics" << std::endl 146 | << "----------------" << std::endl; 147 | output_.format(*os_); 148 | *os_ 149 | << std::endl 150 | << "Solution" << std::endl 151 | << "--------" << std::endl; 152 | output_.solution.format(*os_, parameters_.verbosity_level); 153 | } 154 | -------------------------------------------------------------------------------- /src/subset_sum/algorithms/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(KnapsackSolver_subset_sum_dynamic_programming_bellman) 2 | target_sources(KnapsackSolver_subset_sum_dynamic_programming_bellman PRIVATE 3 | dynamic_programming_bellman.cpp) 4 | target_include_directories(KnapsackSolver_subset_sum_dynamic_programming_bellman PUBLIC 5 | ${PROJECT_SOURCE_DIR}/include) 6 | target_link_libraries(KnapsackSolver_subset_sum_dynamic_programming_bellman PUBLIC 7 | KnapsackSolver_subset_sum) 8 | add_library(KnapsackSolver::subset_sum::dynamic_programming_bellman ALIAS KnapsackSolver_subset_sum_dynamic_programming_bellman) 9 | 10 | add_library(KnapsackSolver_subset_sum_dynamic_programming_balancing) 11 | target_sources(KnapsackSolver_subset_sum_dynamic_programming_balancing PRIVATE 12 | dynamic_programming_balancing.cpp) 13 | target_include_directories(KnapsackSolver_subset_sum_dynamic_programming_balancing PUBLIC 14 | ${PROJECT_SOURCE_DIR}/include) 15 | target_link_libraries(KnapsackSolver_subset_sum_dynamic_programming_balancing PUBLIC 16 | KnapsackSolver_subset_sum) 17 | add_library(KnapsackSolver::subset_sum::dynamic_programming_balancing ALIAS KnapsackSolver_subset_sum_dynamic_programming_balancing) 18 | 19 | add_library(KnapsackSolver_subset_sum_dynamic_programming_primal_dual) 20 | target_sources(KnapsackSolver_subset_sum_dynamic_programming_primal_dual PRIVATE 21 | dynamic_programming_primal_dual.cpp) 22 | target_include_directories(KnapsackSolver_subset_sum_dynamic_programming_primal_dual PUBLIC 23 | ${PROJECT_SOURCE_DIR}/include) 24 | target_link_libraries(KnapsackSolver_subset_sum_dynamic_programming_primal_dual PUBLIC 25 | KnapsackSolver_subset_sum) 26 | add_library(KnapsackSolver::subset_sum::dynamic_programming_primal_dual ALIAS KnapsackSolver_subset_sum_dynamic_programming_primal_dual) 27 | -------------------------------------------------------------------------------- /src/subset_sum/algorithms/dynamic_programming_balancing.cpp: -------------------------------------------------------------------------------- 1 | #include "knapsacksolver/subset_sum/algorithms/dynamic_programming_balancing.hpp" 2 | 3 | #include "knapsacksolver/subset_sum/algorithm_formatter.hpp" 4 | 5 | using namespace knapsacksolver::subset_sum; 6 | 7 | //////////////////////////////////////////////////////////////////////////////// 8 | ///////////////////// dynamic_programming_balancing_array ////////////////////// 9 | //////////////////////////////////////////////////////////////////////////////// 10 | 11 | Output knapsacksolver::subset_sum::dynamic_programming_balancing_array( 12 | const Instance& instance, 13 | const Parameters& parameters) 14 | { 15 | Output output(instance); 16 | AlgorithmFormatter algorithm_formatter(parameters, output); 17 | algorithm_formatter.start("Dynamic programming - balancing - array"); 18 | algorithm_formatter.print_header(); 19 | 20 | Weight c = instance.capacity(); 21 | Weight w_max = 0; 22 | for (ItemId item_id = 0; item_id < instance.number_of_items(); ++item_id) 23 | w_max = std::max(w_max, instance.weight(item_id)); 24 | // Compute the break item and the weight of the break solution. 25 | Weight w_break = 0; 26 | ItemId break_item_id = 0; 27 | for (ItemId item_id = 0; 28 | item_id < instance.number_of_items(); 29 | ++item_id) { 30 | Weight wj = instance.weight(item_id); 31 | if (w_break + wj > c) { 32 | break_item_id = item_id; 33 | break; 34 | } 35 | w_break += wj; 36 | } 37 | // Create and initialize the tables. 38 | std::vector values_pred(w_max * 2, -1); 39 | std::vector values_next(w_max * 2, -1); 40 | Weight offset = c - w_max + 1; 41 | for (Weight w = c - w_max + 1 - offset; w <= c - offset; ++w) 42 | values_pred[w] = 0; 43 | for (Weight w = c + 1 - offset; w <= c + w_max - offset; ++w) 44 | values_pred[w] = 1; 45 | values_pred[w_break - offset] = break_item_id; 46 | 47 | for (ItemId item_id = break_item_id; 48 | item_id < instance.number_of_items(); 49 | ++item_id) { 50 | 51 | // Check time 52 | if (parameters.timer.needs_to_end()) { 53 | algorithm_formatter.end(); 54 | return output; 55 | } 56 | 57 | Weight wj = instance.weight(item_id); 58 | for (Weight w = c - w_max + 1 - offset; w <= c + w_max - offset; ++w) 59 | values_next[w] = values_pred[w]; 60 | for (Weight w = c - w_max + 1 - offset; w <= c - offset; ++w) 61 | values_next[w + wj] = std::max(values_next[w + wj], values_pred[w]); 62 | for (Weight w = c + wj - offset; w >= c + 1 - offset; --w) { 63 | for (ItemId item_id_0 = values_pred[w]; 64 | item_id_0 < values_next[w]; 65 | ++item_id_0) { 66 | Weight wj0 = instance.weight(item_id_0); 67 | values_next[w - wj0] = std::max(values_next[w - wj0], item_id); 68 | } 69 | } 70 | 71 | values_pred.swap(values_next); 72 | 73 | // If optimum reached, stop. 74 | if (values_pred[c - offset] != 0) 75 | break; 76 | } 77 | 78 | // Retrieve optimal value. 79 | Weight optimal_value = 0; 80 | for (Weight weight = c - offset; 81 | weight >= c - w_max + 1 - offset; 82 | --weight) { 83 | if (values_pred[weight] != 0) { 84 | optimal_value = weight + offset; 85 | break; 86 | } 87 | } 88 | // Update lower bound. 89 | algorithm_formatter.update_value( 90 | optimal_value, 91 | "algorithm end (value)"); 92 | // Update upper bound. 93 | algorithm_formatter.update_bound( 94 | optimal_value, 95 | "algorithm end (bound)"); 96 | 97 | algorithm_formatter.end(); 98 | return output; 99 | } 100 | -------------------------------------------------------------------------------- /src/subset_sum/algorithms/dynamic_programming_primal_dual.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fontanf/knapsacksolver/d395f7bcbdde5cdd6e27fadfb2271ae1d0d91c94/src/subset_sum/algorithms/dynamic_programming_primal_dual.cpp -------------------------------------------------------------------------------- /src/subset_sum/generator.cpp: -------------------------------------------------------------------------------- 1 | #include "knapsacksolver/subset_sum/generator.hpp" 2 | 3 | #include "knapsacksolver/subset_sum/instance_builder.hpp" 4 | 5 | #include 6 | 7 | using namespace knapsacksolver::subset_sum; 8 | 9 | namespace 10 | { 11 | 12 | template 13 | T gcd(T a, T b) 14 | { 15 | if (b == 0) 16 | return a; 17 | return gcd(b, a % b); 18 | } 19 | 20 | } 21 | 22 | Instance knapsacksolver::subset_sum::generate_pthree( 23 | ItemPos number_of_items, 24 | std::mt19937_64& generator) 25 | { 26 | InstanceBuilder instance_builder; 27 | std::uniform_int_distribution distribution(1, 1e3); 28 | instance_builder.set_capacity(number_of_items * 1e3 / 4); 29 | for (ItemPos pos = 0; pos < number_of_items; ++pos) 30 | instance_builder.add_item(distribution(generator)); 31 | return instance_builder.build(); 32 | } 33 | 34 | Instance knapsacksolver::subset_sum::generate_psix( 35 | ItemPos number_of_items, 36 | std::mt19937_64& generator) 37 | { 38 | InstanceBuilder instance_builder; 39 | std::uniform_int_distribution distribution(1, 1e6); 40 | instance_builder.set_capacity(number_of_items * 1e6 / 4); 41 | for (ItemPos pos = 0; pos < number_of_items; ++pos) 42 | instance_builder.add_item(distribution(generator)); 43 | return instance_builder.build(); 44 | } 45 | 46 | Instance knapsacksolver::subset_sum::generate_evenodd( 47 | ItemPos number_of_items, 48 | std::mt19937_64& generator) 49 | { 50 | InstanceBuilder instance_builder; 51 | std::uniform_int_distribution distribution(1, 1e3 / 2); 52 | instance_builder.set_capacity(2 * (number_of_items * 1e3 / 8) + 1); 53 | for (ItemPos pos = 0; pos < number_of_items; ++pos) 54 | instance_builder.add_item(2 * distribution(generator)); 55 | return instance_builder.build(); 56 | } 57 | 58 | Instance knapsacksolver::subset_sum::generate_avis( 59 | ItemPos number_of_items) 60 | { 61 | ItemPos n = number_of_items; 62 | InstanceBuilder instance_builder; 63 | std::uniform_int_distribution distribution(1, 1e3); 64 | instance_builder.set_capacity(n * (n + 1) * ((n - 1) / 2) + n * (n - 1) / 2); 65 | for (ItemPos pos = 0; pos < number_of_items; ++pos) 66 | instance_builder.add_item(n * (n + 1) + pos); 67 | return instance_builder.build(); 68 | } 69 | 70 | Instance knapsacksolver::subset_sum::generate_todd( 71 | ItemPos number_of_items) 72 | { 73 | ItemPos n = number_of_items; 74 | InstanceBuilder instance_builder; 75 | std::uniform_int_distribution distribution(1, 1e3); 76 | Weight weight_sum = 0; 77 | for (ItemPos pos = 0; pos < number_of_items; ++pos) { 78 | int i1 = (int)std::floor(std::log(n)) + n + 1; 79 | int i2 = (int)std::floor(std::log(n)) + pos; 80 | Weight item_weight = (1 << i1) + (1 << i2) + 1; 81 | instance_builder.add_item(item_weight); 82 | weight_sum += item_weight; 83 | } 84 | instance_builder.set_capacity(weight_sum / 2); 85 | return instance_builder.build(); 86 | } 87 | 88 | Instance knapsacksolver::subset_sum::generate_somatoth( 89 | ItemPos number_of_items, 90 | std::mt19937_64& generator) 91 | { 92 | InstanceBuilder instance_builder; 93 | 94 | std::uniform_int_distribution distribution(1, number_of_items); 95 | Weight w1; 96 | Weight w2; 97 | Weight capacity; 98 | for (;;) { 99 | w1 = distribution(generator); 100 | w2 = distribution(generator); 101 | std::cout << "w1 " << w1 << " w2 " << w2 << " gcd " << gcd(w1, w2) << std::endl; 102 | if (gcd(w1, w2) != 1) 103 | continue; 104 | 105 | capacity = (w1 - 1) * (w2 - 1) - 1; 106 | std::cout << capacity / w1 << " " << capacity / w2 << " " << number_of_items / 2 << std::endl; 107 | if (capacity / w1 >= number_of_items / 2) 108 | continue; 109 | if (capacity / w2 >= number_of_items / 2) 110 | continue; 111 | } 112 | 113 | instance_builder.set_capacity(capacity); 114 | for (ItemPos pos = 0; pos < number_of_items; ++pos) { 115 | if (pos % 2 == 0) { 116 | instance_builder.add_item(std::ceil((double)pos / 2) * w1); 117 | } else { 118 | instance_builder.add_item(std::ceil((double)pos / 2) * w2); 119 | } 120 | } 121 | 122 | return instance_builder.build(); 123 | } 124 | 125 | Instance knapsacksolver::subset_sum::generate_evenodd6( 126 | ItemPos number_of_items, 127 | std::mt19937_64& generator) 128 | { 129 | InstanceBuilder instance_builder; 130 | std::uniform_int_distribution distribution(1, 1e6 / 2); 131 | instance_builder.set_capacity((number_of_items * 1e6 / 60) + 1); 132 | for (ItemPos pos = 0; pos < number_of_items; ++pos) 133 | instance_builder.add_item(2 * distribution(generator)); 134 | return instance_builder.build(); 135 | } 136 | 137 | Instance knapsacksolver::subset_sum::generate_evenodd8( 138 | ItemPos number_of_items, 139 | std::mt19937_64& generator) 140 | { 141 | InstanceBuilder instance_builder; 142 | std::uniform_int_distribution distribution(1, 1e8 / 2); 143 | Weight weight_sum = 0; 144 | for (ItemPos pos = 0; pos < number_of_items; ++pos) { 145 | Weight item_weight = 2 * distribution(generator); 146 | instance_builder.add_item(item_weight); 147 | weight_sum += item_weight; 148 | } 149 | instance_builder.set_capacity(weight_sum / 2 + 1); 150 | return instance_builder.build(); 151 | } 152 | 153 | Instance knapsacksolver::subset_sum::generate_tenfive6( 154 | ItemPos number_of_items, 155 | std::mt19937_64& generator) 156 | { 157 | InstanceBuilder instance_builder; 158 | std::uniform_int_distribution distribution(1, 1e5); 159 | Weight weight_sum = 0; 160 | for (ItemPos pos = 0; pos < number_of_items; ++pos) { 161 | Weight item_weight = 10 * distribution(generator); 162 | instance_builder.add_item(item_weight); 163 | weight_sum += item_weight; 164 | } 165 | instance_builder.set_capacity((weight_sum / 600) * 10 + 5); 166 | return instance_builder.build(); 167 | } 168 | 169 | Instance knapsacksolver::subset_sum::generate_tenfive8( 170 | ItemPos number_of_items, 171 | std::mt19937_64& generator) 172 | { 173 | InstanceBuilder instance_builder; 174 | std::uniform_int_distribution distribution(1, 1e7); 175 | Weight weight_sum = 0; 176 | for (ItemPos pos = 0; pos < number_of_items; ++pos) { 177 | Weight item_weight = 10 * distribution(generator); 178 | instance_builder.add_item(item_weight); 179 | weight_sum += item_weight; 180 | } 181 | instance_builder.set_capacity((weight_sum / 20) * 10 + 5); 182 | return instance_builder.build(); 183 | } 184 | -------------------------------------------------------------------------------- /src/subset_sum/generator_main.cpp: -------------------------------------------------------------------------------- 1 | #include "knapsacksolver/subset_sum/generator.hpp" 2 | 3 | using namespace knapsacksolver::subset_sum; 4 | 5 | int main(int argc, char *argv[]) 6 | { 7 | (void)argc; 8 | (void)argv; 9 | std::mt19937_64 generator; 10 | 11 | // pthree. 12 | std::cout << "Generate pthree..." << std::endl; 13 | for (ItemPos n: {10, 30, 100, 300, 1000, 3000, 10000}) { 14 | for (int seed = 0; seed < 100; ++seed) { 15 | generator.seed(seed); 16 | Instance instance = generate_pthree(n, generator); 17 | std::string instance_path 18 | = "data/subset_sum/pthree/pthree_" 19 | + std::to_string(n) + "_" + std::to_string(seed); 20 | instance.write(instance_path); 21 | } 22 | } 23 | 24 | // psix. 25 | std::cout << "Generate psix..." << std::endl; 26 | for (ItemPos n: {10, 30, 100, 300, 1000, 3000}) { 27 | for (int seed = 0; seed < 100; ++seed) { 28 | generator.seed(seed); 29 | Instance instance = generate_psix(n, generator); 30 | std::string instance_path 31 | = "data/subset_sum/psix/psix_" 32 | + std::to_string(n) + "_" + std::to_string(seed); 33 | instance.write(instance_path); 34 | } 35 | } 36 | 37 | // evenodd. 38 | std::cout << "Generate evenodd..." << std::endl; 39 | for (ItemPos n: {10, 30, 100, 300, 1000, 3000, 10000}) { 40 | for (int seed = 0; seed < 100; ++seed) { 41 | generator.seed(seed); 42 | Instance instance = generate_evenodd(n, generator); 43 | std::string instance_path 44 | = "data/subset_sum/evenodd/evenodd_" 45 | + std::to_string(n) + "_" + std::to_string(seed); 46 | instance.write(instance_path); 47 | } 48 | } 49 | 50 | // avis. 51 | std::cout << "Generate avis..." << std::endl; 52 | for (ItemPos n: {10, 30, 100, 300, 1000}) { 53 | generator.seed(0); 54 | Instance instance = generate_avis(n); 55 | std::string instance_path 56 | = "data/subset_sum/avis/avis_" 57 | + std::to_string(n); 58 | instance.write(instance_path); 59 | } 60 | 61 | // todd. 62 | std::cout << "Generate todd..." << std::endl; 63 | for (ItemPos n: {10}) { 64 | generator.seed(0); 65 | Instance instance = generate_todd(n); 66 | std::string instance_path 67 | = "data/subset_sum/todd/todd_" 68 | + std::to_string(n); 69 | instance.write(instance_path); 70 | } 71 | 72 | // somatoth. 73 | /* 74 | std::cout << "Generate somatoth..." << std::endl; 75 | for (ItemPos n: {10, 30, 100, 300, 1000, 3000, 10000}) { 76 | std::cout << "n " << n << std::endl; 77 | for (int seed = 0; seed < 100; ++seed) { 78 | generator.seed(seed); 79 | Instance instance = generate_somatoth(n, generator); 80 | std::string instance_path 81 | = "data/subset_sum/somatoth/somatoth_" 82 | + std::to_string(n) + "_" + std::to_string(seed); 83 | instance.write(instance_path); 84 | } 85 | } 86 | */ 87 | 88 | // evenodd6. 89 | std::cout << "Generate evenodd6..." << std::endl; 90 | for (ItemPos n: {1000, 3000, 5000, 8000, 10000, 20000, 30000, 40000, 50000, 60000, 70000, 80000, 90000, 100000}) { 91 | for (int seed = 0; seed < 1; ++seed) { 92 | generator.seed(seed); 93 | Instance instance = generate_evenodd6(n, generator); 94 | std::string instance_path 95 | = "data/subset_sum/evenodd6/evenodd6_" 96 | + std::to_string(n) + "_" + std::to_string(seed); 97 | instance.write(instance_path); 98 | } 99 | } 100 | 101 | // evenodd8. 102 | std::cout << "Generate evenodd8..." << std::endl; 103 | for (ItemPos n: {10, 50, 100, 150, 200, 300, 500, 1000, 5000, 10000, 50000}) { 104 | for (int seed = 0; seed < 1; ++seed) { 105 | generator.seed(seed); 106 | Instance instance = generate_evenodd8(n, generator); 107 | std::string instance_path 108 | = "data/subset_sum/evenodd8/evenodd8_" 109 | + std::to_string(n) + "_" + std::to_string(seed); 110 | instance.write(instance_path); 111 | } 112 | } 113 | 114 | // tenfive6. 115 | std::cout << "Generate tenfive6..." << std::endl; 116 | for (ItemPos n: {1000, 3000, 5000, 8000, 10000, 20000, 30000, 40000, 50000, 60000, 70000, 80000, 90000, 100000}) { 117 | for (int seed = 0; seed < 1; ++seed) { 118 | generator.seed(seed); 119 | Instance instance = generate_tenfive6(n, generator); 120 | std::string instance_path 121 | = "data/subset_sum/tenfive6/tenfive6_" 122 | + std::to_string(n) + "_" + std::to_string(seed); 123 | instance.write(instance_path); 124 | } 125 | } 126 | 127 | // tenfive8. 128 | std::cout << "Generate tenfive8..." << std::endl; 129 | for (ItemPos n: {10, 50, 100, 150, 200, 300, 500, 1000, 5000, 10000, 50000}) { 130 | for (int seed = 0; seed < 1; ++seed) { 131 | generator.seed(seed); 132 | Instance instance = generate_tenfive8(n, generator); 133 | std::string instance_path 134 | = "data/subset_sum/tenfive8/tenfive8_" 135 | + std::to_string(n) + "_" + std::to_string(seed); 136 | instance.write(instance_path); 137 | } 138 | } 139 | 140 | return 0; 141 | } 142 | -------------------------------------------------------------------------------- /src/subset_sum/instance.cpp: -------------------------------------------------------------------------------- 1 | #include "knapsacksolver/subset_sum/instance.hpp" 2 | 3 | #include 4 | 5 | using namespace knapsacksolver::subset_sum; 6 | 7 | void Instance::format( 8 | std::ostream& os, 9 | int verbosity_level) const 10 | { 11 | if (verbosity_level >= 1) { 12 | os 13 | << "Number of items: " << number_of_items() << std::endl 14 | << "Capacity: " << capacity() << std::endl 15 | ; 16 | } 17 | 18 | if (verbosity_level >= 2) { 19 | os 20 | << std::endl 21 | << std::setw(12) << "Item" 22 | << std::setw(12) << "Weight" 23 | << std::endl 24 | << std::setw(12) << "----" 25 | << std::setw(12) << "------" 26 | << std::endl; 27 | for (ItemId item_id = 0; item_id < number_of_items(); ++item_id) { 28 | os 29 | << std::setw(12) << item_id 30 | << std::setw(12) << weight(item_id) 31 | << std::endl; 32 | } 33 | } 34 | } 35 | 36 | void Instance::write( 37 | std::string instance_path) const 38 | { 39 | std::ofstream file(instance_path); 40 | if (!file.good()) { 41 | throw std::runtime_error( 42 | "Unable to open file \"" + instance_path + "\"."); 43 | } 44 | 45 | file << number_of_items() << " " << capacity() << std::endl; 46 | for (ItemId item_id = 0; item_id < number_of_items(); ++item_id) 47 | file << weight(item_id) << std::endl; 48 | } 49 | -------------------------------------------------------------------------------- /src/subset_sum/instance_builder.cpp: -------------------------------------------------------------------------------- 1 | #include "knapsacksolver/subset_sum/instance_builder.hpp" 2 | 3 | #include 4 | 5 | using namespace knapsacksolver::subset_sum; 6 | 7 | void InstanceBuilder::read( 8 | const std::string& instance_path, 9 | const std::string& format) 10 | { 11 | std::ifstream file(instance_path); 12 | if (!file.good()) { 13 | throw std::runtime_error( 14 | "Unable to open file \"" + instance_path + "\"."); 15 | } 16 | 17 | if (format == "standard" || format == "") { 18 | read_standard(file); 19 | } else { 20 | throw std::invalid_argument( 21 | "Unknown instance format \"" + format + "\"."); 22 | } 23 | 24 | file.close(); 25 | } 26 | 27 | void InstanceBuilder::read_standard(std::ifstream& file) 28 | { 29 | ItemId number_of_items; 30 | Weight weight; 31 | file >> number_of_items >> weight; 32 | set_capacity(weight); 33 | for (ItemPos item_id = 0; item_id < number_of_items; ++item_id) { 34 | file >> weight; 35 | add_item(weight); 36 | } 37 | } 38 | 39 | Instance InstanceBuilder::build() 40 | { 41 | return std::move(instance_); 42 | } 43 | -------------------------------------------------------------------------------- /src/subset_sum/main.cpp: -------------------------------------------------------------------------------- 1 | #include "knapsacksolver/subset_sum/instance_builder.hpp" 2 | 3 | #include "knapsacksolver/subset_sum/algorithms/dynamic_programming_bellman.hpp" 4 | #include "knapsacksolver/subset_sum/algorithms/dynamic_programming_balancing.hpp" 5 | 6 | #include 7 | 8 | using namespace knapsacksolver::subset_sum; 9 | 10 | namespace po = boost::program_options; 11 | 12 | void read_args( 13 | Parameters& parameters, 14 | const po::variables_map& vm) 15 | { 16 | parameters.timer.set_sigint_handler(); 17 | parameters.messages_to_stdout = true; 18 | if (vm.count("time-limit")) 19 | parameters.timer.set_time_limit(vm["time-limit"].as()); 20 | if (vm.count("verbosity-level")) 21 | parameters.verbosity_level = vm["verbosity-level"].as(); 22 | if (vm.count("log")) 23 | parameters.log_path = vm["log"].as(); 24 | parameters.log_to_stderr = vm.count("log-to-stderr"); 25 | bool only_write_at_the_end = vm.count("only-write-at-the-end"); 26 | if (!only_write_at_the_end) { 27 | 28 | std::string certificate_path; 29 | if (vm.count("certificate")) 30 | certificate_path = vm["certificate"].as(); 31 | 32 | std::string json_output_path; 33 | if (vm.count("output")) 34 | json_output_path = vm["output"].as(); 35 | 36 | parameters.new_solution_callback = [ 37 | json_output_path, 38 | certificate_path]( 39 | const Output& output) 40 | { 41 | if (!json_output_path.empty()) 42 | output.write_json_output(json_output_path); 43 | if (!certificate_path.empty()) 44 | output.solution.write(certificate_path); 45 | }; 46 | } 47 | } 48 | 49 | Output run( 50 | const Instance& instance, 51 | const po::variables_map& vm) 52 | { 53 | std::mt19937_64 generator(vm["seed"].as()); 54 | Solution solution = (!vm.count("initial-solution"))? 55 | Solution(instance): 56 | Solution(instance, vm["initial-solution"].as()); 57 | 58 | // Run algorithm. 59 | std::string algorithm = vm["algorithm"].as(); 60 | if (algorithm == "dynamic-programming-bellman-array") { 61 | Parameters parameters; 62 | read_args(parameters, vm); 63 | return dynamic_programming_bellman_array(instance, parameters); 64 | } else if (algorithm == "dynamic-programming-bellman-list") { 65 | Parameters parameters; 66 | read_args(parameters, vm); 67 | return dynamic_programming_bellman_list(instance, parameters); 68 | } else if (algorithm == "dynamic-programming-bellman-word-ram") { 69 | Parameters parameters; 70 | read_args(parameters, vm); 71 | return dynamic_programming_bellman_word_ram(instance, parameters); 72 | } else if (algorithm == "dynamic-programming-bellman-word-ram-rec") { 73 | Parameters parameters; 74 | read_args(parameters, vm); 75 | return dynamic_programming_bellman_word_ram_rec(instance, parameters); 76 | 77 | } else if (algorithm == "dynamic-programming-balancing-array") { 78 | Parameters parameters; 79 | read_args(parameters, vm); 80 | return dynamic_programming_balancing_array(instance, parameters); 81 | 82 | } else { 83 | throw std::invalid_argument( 84 | "Unknown algorithm \"" + algorithm + "\"."); 85 | } 86 | } 87 | 88 | int main(int argc, char *argv[]) 89 | { 90 | // Parse program options 91 | po::options_description desc("Allowed options"); 92 | desc.add_options() 93 | ("help,h", "produce help message") 94 | ("algorithm,a", po::value(), "set algorithm") 95 | ("input,i", po::value()->required(), "set input file (required)") 96 | ("format,f", po::value()->default_value(""), "set input file format (default: standard)") 97 | ("output,o", po::value(), "set JSON output file") 98 | ("certificate,c", po::value(), "set certificate file") 99 | ("seed,s", po::value()->default_value(0), "set seed") 100 | ("time-limit,t", po::value(), "set time limit in seconds") 101 | ("verbosity-level,v", po::value(), "set verbosity level") 102 | ("only-write-at-the-end,e", "only write output and certificate files at the end") 103 | ("log,l", po::value(), "set log file") 104 | ("log-to-stderr", "write log to stderr") 105 | ; 106 | po::variables_map vm; 107 | po::store(po::parse_command_line(argc, argv, desc), vm); 108 | if (vm.count("help")) { 109 | std::cout << desc << std::endl;; 110 | return 1; 111 | } 112 | try { 113 | po::notify(vm); 114 | } catch (const po::required_option& e) { 115 | std::cout << desc << std::endl; 116 | return 1; 117 | } 118 | 119 | // Build instance. 120 | InstanceBuilder instance_builder; 121 | instance_builder.read( 122 | vm["input"].as(), 123 | vm["format"].as()); 124 | const Instance instance = instance_builder.build(); 125 | 126 | // Run. 127 | Output output = run(instance, vm); 128 | 129 | // Write outputs. 130 | if (vm.count("certificate")) 131 | output.solution.write(vm["certificate"].as()); 132 | if (vm.count("output")) 133 | output.write_json_output(vm["output"].as()); 134 | 135 | return 0; 136 | } 137 | -------------------------------------------------------------------------------- /src/subset_sum/solution.cpp: -------------------------------------------------------------------------------- 1 | #include "knapsacksolver/subset_sum/solution.hpp" 2 | 3 | #include "optimizationtools/utils/utils.hpp" 4 | 5 | using namespace knapsacksolver::subset_sum; 6 | 7 | Solution::Solution(const Instance& instance): 8 | instance_(&instance), 9 | contains_(instance.number_of_items(), 0) 10 | { } 11 | 12 | Solution::Solution( 13 | const Instance& instance, 14 | const std::string& certificate_path): 15 | Solution(instance) 16 | { 17 | if (certificate_path.empty()) 18 | return; 19 | std::ifstream file(certificate_path); 20 | if (!file.good()) { 21 | throw std::runtime_error( 22 | "Unable to open file \"" + certificate_path + "\"."); 23 | } 24 | 25 | ItemPos number_of_items; 26 | ItemPos item_id; 27 | file >> number_of_items; 28 | for (ItemPos pos = 0; pos < number_of_items; ++pos) { 29 | file >> item_id; 30 | add(item_id); 31 | } 32 | } 33 | 34 | void Solution::add(ItemId item_id) 35 | { 36 | contains_[item_id] = 1; 37 | number_of_items_++; 38 | weight_ += instance().weight(item_id); 39 | } 40 | 41 | void Solution::write( 42 | const std::string& certificate_path) const 43 | { 44 | if (certificate_path.empty()) 45 | return; 46 | std::ofstream file(certificate_path); 47 | if (!file.good()) { 48 | throw std::runtime_error( 49 | "Unable to open file \"" + certificate_path + "\"."); 50 | } 51 | 52 | file << number_of_items() << std::endl; 53 | for (ItemId item_id = 0; item_id < instance().number_of_items(); ++item_id) 54 | if (contains(item_id)) 55 | file << item_id << std::endl; 56 | } 57 | 58 | void Solution::format( 59 | std::ostream& os, 60 | int verbosity_level) const 61 | { 62 | if (verbosity_level >= 1) { 63 | os 64 | << "Number of items: " << optimizationtools::Ratio(number_of_items(), instance().number_of_items()) << std::endl 65 | << "Weight: " << optimizationtools::Ratio(weight(), instance().capacity()) << std::endl 66 | << "Feasible: " << feasible() << std::endl 67 | ; 68 | } 69 | 70 | if (verbosity_level >= 2) { 71 | os << std::endl 72 | << std::setw(12) << "Item" 73 | << std::endl 74 | << std::setw(12) << "----" 75 | << std::endl; 76 | for (ItemId item_id = 0; 77 | item_id < number_of_items(); 78 | ++item_id) { 79 | os 80 | << std::setw(12) << item_id 81 | << std::endl; 82 | } 83 | } 84 | } 85 | 86 | nlohmann::json Solution::to_json() const 87 | { 88 | return nlohmann::json { 89 | {"NumberOfItems", number_of_items()}, 90 | {"Feasible", feasible()}, 91 | {"Weight", weight()}, 92 | }; 93 | } 94 | -------------------------------------------------------------------------------- /src/subset_sum/tests.cpp: -------------------------------------------------------------------------------- 1 | #include "knapsacksolver/subset_sum/tests.hpp" 2 | 3 | #include "knapsacksolver/subset_sum/instance_builder.hpp" 4 | 5 | #include 6 | 7 | using namespace knapsacksolver::subset_sum; 8 | 9 | namespace fs = boost::filesystem; 10 | 11 | std::string knapsacksolver::subset_sum::get_path( 12 | const std::vector& path) 13 | { 14 | if (path.empty()) 15 | return ""; 16 | fs::path p(path[0]); 17 | for (size_t i = 1; i < path.size(); ++i) 18 | p /= path[i]; 19 | return p.string(); 20 | } 21 | 22 | std::vector knapsacksolver::subset_sum::get_pthree_instance_paths( 23 | ItemId number_of_items) 24 | { 25 | std::vector instance_paths; 26 | for (int i = 0; i < 100; ++i) { 27 | instance_paths.push_back({ 28 | get_path({"pthree", "pthree_" + std::to_string(number_of_items) + "_" + std::to_string(i)}), "standard", 29 | get_path({"pthree", "pthree_" + std::to_string(number_of_items) + "_" + std::to_string(i) + "_solution.txt"}), "standard"}); 30 | } 31 | return instance_paths; 32 | } 33 | 34 | std::vector knapsacksolver::subset_sum::get_psix_instance_paths( 35 | ItemId number_of_items) 36 | { 37 | std::vector instance_paths; 38 | for (int i = 0; i < 100; ++i) { 39 | instance_paths.push_back({ 40 | get_path({"psix", "psix_" + std::to_string(number_of_items) + "_" + std::to_string(i)}), "standard", 41 | get_path({"psix", "psix_" + std::to_string(number_of_items) + "_" + std::to_string(i) + "_solution.txt"}), "standard"}); 42 | } 43 | return instance_paths; 44 | } 45 | 46 | std::vector knapsacksolver::subset_sum::get_test_params( 47 | const std::vector& algorithms, 48 | const std::vector>& instance_paths) 49 | { 50 | std::vector res; 51 | for (const Algorithm& algorithm: algorithms) { 52 | for (const auto& v: instance_paths) { 53 | for (const TestInstancePath& files: v) { 54 | TestParams test_params; 55 | test_params.algorithm = algorithm; 56 | test_params.files = files; 57 | res.push_back(test_params); 58 | } 59 | } 60 | } 61 | return res; 62 | } 63 | 64 | const Instance knapsacksolver::subset_sum::get_instance( 65 | const TestInstancePath& files) 66 | { 67 | InstanceBuilder instance_builder; 68 | std::string instance_path = get_path({ 69 | std::getenv("SUBSET_SUM_DATA"), 70 | files.instance_path}); 71 | std::cout << "Instance path: " << instance_path << std::endl; 72 | instance_builder.read( 73 | instance_path, 74 | files.instance_format); 75 | return instance_builder.build(); 76 | } 77 | 78 | const Solution knapsacksolver::subset_sum::get_solution( 79 | const Instance& instance, 80 | const TestInstancePath& files) 81 | { 82 | std::string certificate_path = get_path({ 83 | std::getenv("SUBSET_SUM_DATA"), 84 | files.certificate_path}); 85 | return Solution( 86 | instance, 87 | certificate_path); 88 | } 89 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | enable_testing() 2 | 3 | include(GoogleTest) 4 | 5 | add_subdirectory(knapsack) 6 | add_subdirectory(subset_sum) 7 | add_subdirectory(multiple_choice_subset_sum) 8 | -------------------------------------------------------------------------------- /test/knapsack/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(algorithms) 2 | -------------------------------------------------------------------------------- /test/knapsack/algorithms/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(KnapsackSolver_knapsack_dynamic_programming_bellman_test) 2 | target_sources(KnapsackSolver_knapsack_dynamic_programming_bellman_test PRIVATE 3 | dynamic_programming_bellman_test.cpp) 4 | target_link_libraries(KnapsackSolver_knapsack_dynamic_programming_bellman_test 5 | KnapsackSolver_knapsack_dynamic_programming_bellman 6 | KnapsackSolver_knapsack_tests 7 | GTest::gtest_main) 8 | gtest_discover_tests(KnapsackSolver_knapsack_dynamic_programming_bellman_test) 9 | 10 | add_executable(KnapsackSolver_knapsack_dynamic_programming_primal_dual_test) 11 | target_sources(KnapsackSolver_knapsack_dynamic_programming_primal_dual_test PRIVATE 12 | dynamic_programming_primal_dual_test.cpp) 13 | target_link_libraries(KnapsackSolver_knapsack_dynamic_programming_primal_dual_test 14 | KnapsackSolver_knapsack_dynamic_programming_primal_dual 15 | KnapsackSolver_knapsack_tests 16 | GTest::gtest_main) 17 | add_test(KnapsackSolver_knapsack_dynamic_programming_primal_dual_test KnapsackSolver_knapsack_dynamic_programming_primal_dual_test) 18 | set_tests_properties(KnapsackSolver_knapsack_dynamic_programming_primal_dual_test PROPERTIES TIMEOUT 10000) 19 | -------------------------------------------------------------------------------- /test/knapsack/algorithms/dynamic_programming_bellman_test.cpp: -------------------------------------------------------------------------------- 1 | #include "knapsacksolver/knapsack/tests.hpp" 2 | 3 | #include "knapsacksolver/knapsack/algorithms/dynamic_programming_bellman.hpp" 4 | 5 | using namespace knapsacksolver; 6 | using namespace knapsacksolver::knapsack; 7 | 8 | TEST_P(ExactAlgorithmTest, ExactAlgorithm) 9 | { 10 | TestParams test_params = GetParam(); 11 | const Instance instance = get_instance(test_params.files); 12 | const Solution solution = get_solution(instance, test_params.files); 13 | auto output = test_params.algorithm(instance); 14 | EXPECT_EQ(output.value, solution.profit()); 15 | EXPECT_EQ(output.value, output.solution.profit()); 16 | EXPECT_EQ(output.bound, solution.profit()); 17 | } 18 | 19 | TEST_P(ExactNoSolutionAlgorithmTest, ExactNoSolutionAlgorithm) 20 | { 21 | TestParams test_params = GetParam(); 22 | const Instance instance = get_instance(test_params.files); 23 | const Solution solution = get_solution(instance, test_params.files); 24 | auto output = test_params.algorithm(instance); 25 | EXPECT_EQ(output.value, solution.profit()); 26 | EXPECT_EQ(output.bound, solution.profit()); 27 | } 28 | 29 | INSTANTIATE_TEST_SUITE_P( 30 | KnapsackDynamicProgrammingBellmanArray, 31 | ExactNoSolutionAlgorithmTest, 32 | testing::ValuesIn(get_test_params( 33 | { 34 | [](const Instance& instance) 35 | { 36 | return dynamic_programming_bellman_array(instance); 37 | }, 38 | }, 39 | { 40 | get_test_instance_paths(), 41 | get_pisinger_instance_paths("smallcoeff", "knapPI_1_50_1000"), 42 | }))); 43 | 44 | INSTANTIATE_TEST_SUITE_P( 45 | KnapsackDynamicProgrammingBellmanNoSolution, 46 | ExactNoSolutionAlgorithmTest, 47 | testing::ValuesIn(get_test_params( 48 | { 49 | [](const Instance& instance) 50 | { 51 | return dynamic_programming_bellman_rec(instance); 52 | }, 53 | [](const Instance& instance) 54 | { 55 | return dynamic_programming_bellman_array(instance); 56 | }, 57 | [](const Instance& instance) 58 | { 59 | return dynamic_programming_bellman_array_parallel(instance); 60 | }, 61 | [](const Instance& instance) 62 | { 63 | return dynamic_programming_bellman_list(instance); 64 | }, 65 | [](const Instance& instance) 66 | { 67 | DynamicProgrammingBellmanListParameters parameters; 68 | parameters.sort = true; 69 | return dynamic_programming_bellman_list(instance, parameters); 70 | }, 71 | }, 72 | { 73 | get_test_instance_paths(), 74 | }))); 75 | 76 | INSTANTIATE_TEST_SUITE_P( 77 | KnapsackDynamicProgrammingBellman, 78 | ExactAlgorithmTest, 79 | testing::ValuesIn(get_test_params( 80 | { 81 | [](const Instance& instance) 82 | { 83 | return dynamic_programming_bellman_array_all(instance); 84 | }, 85 | [](const Instance& instance) 86 | { 87 | return dynamic_programming_bellman_array_one(instance); 88 | }, 89 | [](const Instance& instance) 90 | { 91 | DynamicProgrammingBellmanArrayPartParameters parameters; 92 | parameters.partial_solution_size = 1; 93 | return dynamic_programming_bellman_array_part(instance, parameters); 94 | }, 95 | [](const Instance& instance) 96 | { 97 | DynamicProgrammingBellmanArrayPartParameters parameters; 98 | parameters.partial_solution_size = 2; 99 | return dynamic_programming_bellman_array_part(instance, parameters); 100 | }, 101 | [](const Instance& instance) 102 | { 103 | DynamicProgrammingBellmanArrayPartParameters parameters; 104 | parameters.partial_solution_size = 3; 105 | return dynamic_programming_bellman_array_part(instance, parameters); 106 | }, 107 | [](const Instance& instance) 108 | { 109 | return dynamic_programming_bellman_array_part(instance); 110 | }, 111 | [](const Instance& instance) 112 | { 113 | return dynamic_programming_bellman_array_rec(instance); 114 | }, 115 | }, 116 | { 117 | get_test_instance_paths(), 118 | }))); 119 | -------------------------------------------------------------------------------- /test/multiple_choice_subset_sum/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(algorithms) 2 | -------------------------------------------------------------------------------- /test/multiple_choice_subset_sum/algorithms/CMakeLists.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fontanf/knapsacksolver/d395f7bcbdde5cdd6e27fadfb2271ae1d0d91c94/test/multiple_choice_subset_sum/algorithms/CMakeLists.txt -------------------------------------------------------------------------------- /test/subset_sum/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(algorithms) 2 | -------------------------------------------------------------------------------- /test/subset_sum/algorithms/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(KnapsackSolver_subset_sum_dynamic_programming_bellman_test) 2 | target_sources(KnapsackSolver_subset_sum_dynamic_programming_bellman_test PRIVATE 3 | dynamic_programming_bellman_test.cpp) 4 | target_link_libraries(KnapsackSolver_subset_sum_dynamic_programming_bellman_test 5 | KnapsackSolver_subset_sum_dynamic_programming_bellman 6 | KnapsackSolver_subset_sum_tests 7 | GTest::gtest_main) 8 | add_test(KnapsackSolver_subset_sum_dynamic_programming_bellman_test KnapsackSolver_subset_sum_dynamic_programming_bellman_test) 9 | -------------------------------------------------------------------------------- /test/subset_sum/algorithms/dynamic_programming_bellman_test.cpp: -------------------------------------------------------------------------------- 1 | #include "knapsacksolver/subset_sum/tests.hpp" 2 | 3 | #include "knapsacksolver/subset_sum/algorithms/dynamic_programming_bellman.hpp" 4 | 5 | using namespace knapsacksolver; 6 | using namespace knapsacksolver::subset_sum; 7 | 8 | TEST_P(ExactAlgorithmTest, ExactAlgorithm) 9 | { 10 | TestParams test_params = GetParam(); 11 | const Instance instance = get_instance(test_params.files); 12 | const Solution solution = get_solution(instance, test_params.files); 13 | auto output = test_params.algorithm(instance); 14 | EXPECT_EQ(output.value, solution.weight()); 15 | EXPECT_EQ(output.value, output.solution.weight()); 16 | EXPECT_EQ(output.bound, solution.weight()); 17 | } 18 | 19 | TEST_P(ExactNoSolutionAlgorithmTest, ExactNoSolutionAlgorithm) 20 | { 21 | TestParams test_params = GetParam(); 22 | const Instance instance = get_instance(test_params.files); 23 | const Solution solution = get_solution(instance, test_params.files); 24 | auto output = test_params.algorithm(instance); 25 | EXPECT_EQ(output.value, solution.weight()); 26 | EXPECT_EQ(output.bound, solution.weight()); 27 | } 28 | 29 | INSTANTIATE_TEST_SUITE_P( 30 | SubsetSumDynamicProgrammingBellmanArray, 31 | ExactAlgorithmTest, 32 | testing::ValuesIn(get_test_params( 33 | { 34 | [](const Instance& instance) 35 | { 36 | return dynamic_programming_bellman_array(instance); 37 | }, 38 | }, 39 | { 40 | get_pthree_instance_paths(10), 41 | get_pthree_instance_paths(30), 42 | get_pthree_instance_paths(100), 43 | get_pthree_instance_paths(300), 44 | get_pthree_instance_paths(1000), 45 | get_psix_instance_paths(10), 46 | }))); 47 | 48 | INSTANTIATE_TEST_SUITE_P( 49 | SubsetSumDynamicProgrammingBellmanList, 50 | ExactNoSolutionAlgorithmTest, 51 | testing::ValuesIn(get_test_params( 52 | { 53 | [](const Instance& instance) 54 | { 55 | return dynamic_programming_bellman_list(instance); 56 | }, 57 | }, 58 | { 59 | get_pthree_instance_paths(10), 60 | get_pthree_instance_paths(30), 61 | get_pthree_instance_paths(100), 62 | get_pthree_instance_paths(300), 63 | get_pthree_instance_paths(1000), 64 | get_psix_instance_paths(10), 65 | }))); 66 | 67 | INSTANTIATE_TEST_SUITE_P( 68 | SubsetSumDynamicProgrammingBellmanWordRam, 69 | ExactNoSolutionAlgorithmTest, 70 | testing::ValuesIn(get_test_params( 71 | { 72 | [](const Instance& instance) 73 | { 74 | return dynamic_programming_bellman_word_ram(instance); 75 | }, 76 | }, 77 | { 78 | get_pthree_instance_paths(10), 79 | get_pthree_instance_paths(30), 80 | get_pthree_instance_paths(100), 81 | get_pthree_instance_paths(300), 82 | get_pthree_instance_paths(1000), 83 | get_psix_instance_paths(10), 84 | }))); 85 | 86 | INSTANTIATE_TEST_SUITE_P( 87 | SubsetSumDynamicProgrammingBellmanWordRamRec, 88 | ExactAlgorithmTest, 89 | testing::ValuesIn(get_test_params( 90 | { 91 | [](const Instance& instance) 92 | { 93 | return dynamic_programming_bellman_word_ram_rec(instance); 94 | }, 95 | }, 96 | { 97 | get_pthree_instance_paths(10), 98 | get_pthree_instance_paths(30), 99 | get_pthree_instance_paths(100), 100 | get_pthree_instance_paths(300), 101 | get_pthree_instance_paths(1000), 102 | get_psix_instance_paths(10), 103 | }))); 104 | --------------------------------------------------------------------------------