diff --git a/include/knapsacksolver/subset_sum/solution.hpp b/include/knapsacksolver/subset_sum/solution.hpp index 8da9bef..7e4e5af 100644 --- a/include/knapsacksolver/subset_sum/solution.hpp +++ b/include/knapsacksolver/subset_sum/solution.hpp @@ -29,7 +29,7 @@ class Solution /** Create a solution from a file. */ Solution( const Instance& instance, - std::string certificate_path); + const std::string& certificate_path); /** Add an item to the solution. */ void add(ItemId item_id); @@ -61,7 +61,7 @@ class Solution */ /** Write the solution to a file. */ - void write(std::string filepath) const; + void write(const std::string& certificate_path) const; /** Export solution characteristics to a JSON structure. */ nlohmann::json to_json() const; diff --git a/include/knapsacksolver/subset_sum/tests.hpp b/include/knapsacksolver/subset_sum/tests.hpp new file mode 100644 index 0000000..6448cfa --- /dev/null +++ b/include/knapsacksolver/subset_sum/tests.hpp @@ -0,0 +1,51 @@ +#pragma once + +#include "knapsacksolver/subset_sum/solution.hpp" + +#include + +namespace knapsacksolver +{ +namespace subset_sum +{ + +std::string get_path(const std::vector& path); + +struct TestInstancePath +{ + std::string instance_path; + std::string instance_format; + std::string certificate_path; + std::string certificate_format; +}; + +std::vector get_pthree_instance_paths( + ItemId number_of_items); + +std::vector get_psix_instance_paths( + ItemId number_of_items); + +using Algorithm = std::function; + +struct TestParams +{ + Algorithm algorithm; + TestInstancePath files; +}; + +std::vector get_test_params( + const std::vector& algorithms, + const std::vector>& instance_paths); + +const Instance get_instance( + const TestInstancePath& files); + +const Solution get_solution( + const Instance& instance, + const TestInstancePath& files); + +class ExactAlgorithmTest: public testing::TestWithParam { }; +class ExactNoSolutionAlgorithmTest: public testing::TestWithParam { }; + +} +} diff --git a/scripts/download_data.py b/scripts/download_data.py index 0be82b3..228482a 100644 --- a/scripts/download_data.py +++ b/scripts/download_data.py @@ -9,4 +9,4 @@ def download(id): pathlib.Path("data.7z").unlink() download("1jCf-Ye7pAQLVep6MT1HA_G05gdEgWeKk") -download("1n64Bti7qgImKDtY_bNAR3oNSC-S80-We") +download("1tNIfm81yBp7DU3qfQVLLnewYwKlyzsrt") diff --git a/src/knapsack/tests.cpp b/src/knapsack/tests.cpp index 8485d7f..7c99ff2 100644 --- a/src/knapsack/tests.cpp +++ b/src/knapsack/tests.cpp @@ -100,10 +100,12 @@ const Instance knapsacksolver::knapsack::get_instance( const TestInstancePath& files) { InstanceBuilder instance_builder; - std::string data_path = std::getenv("KNAPSACK_DATA"); - data_path += "/"; + std::string instance_path = get_path({ + std::getenv("KNAPSACK_DATA"), + files.instance_path}); + std::cout << "Instance path: " << instance_path << std::endl; instance_builder.read( - data_path + files.instance_path, + instance_path, files.instance_format); return instance_builder.build(); } @@ -112,10 +114,11 @@ const Solution knapsacksolver::knapsack::get_solution( const Instance& instance, const TestInstancePath& files) { - std::string data_path = std::getenv("KNAPSACK_DATA"); - data_path += "/"; + std::string certificate_path = get_path({ + std::getenv("KNAPSACK_DATA"), + files.certificate_path}); return Solution( instance, - data_path + files.certificate_path, + certificate_path, files.certificate_format); } diff --git a/src/subset_sum/CMakeLists.txt b/src/subset_sum/CMakeLists.txt index 88e02bd..de4180e 100644 --- a/src/subset_sum/CMakeLists.txt +++ b/src/subset_sum/CMakeLists.txt @@ -29,3 +29,12 @@ target_sources(KnapsackSolver_subset_sum_generator PRIVATE target_link_libraries(KnapsackSolver_subset_sum_generator PUBLIC KnapsackSolver_subset_sum) add_library(KnapsackSolver::subset_sum::generator ALIAS KnapsackSolver_subset_sum_generator) + +add_library(KnapsackSolver_subset_sum_tests) +target_sources(KnapsackSolver_subset_sum_tests PRIVATE + tests.cpp) +target_link_libraries(KnapsackSolver_subset_sum_tests PUBLIC + KnapsackSolver_subset_sum + Boost::filesystem + GTest::gtest_main) +add_library(KnapsackSolver::subset_sum::tests ALIAS KnapsackSolver_subset_sum_tests) diff --git a/src/subset_sum/algorithms/dynamic_programming_bellman.cpp b/src/subset_sum/algorithms/dynamic_programming_bellman.cpp index b6c81c0..3f5ae45 100644 --- a/src/subset_sum/algorithms/dynamic_programming_bellman.cpp +++ b/src/subset_sum/algorithms/dynamic_programming_bellman.cpp @@ -192,7 +192,7 @@ const Output knapsacksolver::subset_sum::dynamic_programming_bellman_word_ram( // std::cout << ((values[word] >> bit) & 1); //std::cout << std::endl; - if (((values[capacity_number_of_words - 1] >> capacity_number_of_bits_to_shift) & 1) == 1) + if (((values[capacity_number_of_words] >> capacity_number_of_bits_to_shift) & 1) == 1) break; } diff --git a/src/subset_sum/main.cpp b/src/subset_sum/main.cpp index 0706733..f50f7ee 100644 --- a/src/subset_sum/main.cpp +++ b/src/subset_sum/main.cpp @@ -24,15 +24,24 @@ void read_args( parameters.log_to_stderr = vm.count("log-to-stderr"); bool only_write_at_the_end = vm.count("only-write-at-the-end"); if (!only_write_at_the_end) { - std::string certificate_path = vm["certificate"].as(); - std::string json_output_path = vm["output"].as(); + + std::string certificate_path; + if (vm.count("certificate")) + certificate_path = vm["certificate"].as(); + + std::string json_output_path; + if (vm.count("output")) + json_output_path = vm["output"].as(); + parameters.new_solution_callback = [ json_output_path, certificate_path]( const Output& output) { - output.write_json_output(json_output_path); - output.solution.write(certificate_path); + if (!json_output_path.empty()) + output.write_json_output(json_output_path); + if (!certificate_path.empty()) + output.solution.write(certificate_path); }; } } @@ -85,8 +94,8 @@ int main(int argc, char *argv[]) ("algorithm,a", po::value(), "set algorithm") ("input,i", po::value()->required(), "set input file (required)") ("format,f", po::value()->default_value(""), "set input file format (default: standard)") - ("output,o", po::value()->default_value(""), "set JSON output file") - ("certificate,c", po::value()->default_value(""), "set certificate file") + ("output,o", po::value(), "set JSON output file") + ("certificate,c", po::value(), "set certificate file") ("seed,s", po::value()->default_value(0), "set seed") ("time-limit,t", po::value(), "set time limit in seconds") ("verbosity-level,v", po::value(), "set verbosity level") @@ -103,7 +112,7 @@ int main(int argc, char *argv[]) try { po::notify(vm); } catch (const po::required_option& e) { - std::cout << desc << std::endl;; + std::cout << desc << std::endl; return 1; } @@ -118,10 +127,10 @@ int main(int argc, char *argv[]) Output output = run(instance, vm); // Write outputs. - std::string certificate_path = vm["certificate"].as(); - std::string json_output_path = vm["output"].as(); - output.write_json_output(json_output_path); - output.solution.write(certificate_path); + if (vm.count("certificate")) + output.solution.write(vm["certificate"].as()); + if (vm.count("output")) + output.write_json_output(vm["output"].as()); return 0; } diff --git a/src/subset_sum/solution.cpp b/src/subset_sum/solution.cpp index e57c887..73d9ccd 100644 --- a/src/subset_sum/solution.cpp +++ b/src/subset_sum/solution.cpp @@ -11,7 +11,7 @@ Solution::Solution(const Instance& instance): Solution::Solution( const Instance& instance, - std::string certificate_path): + const std::string& certificate_path): Solution(instance) { if (certificate_path.empty()) @@ -38,7 +38,8 @@ void Solution::add(ItemId item_id) weight_ += instance().weight(item_id); } -void Solution::write(std::string certificate_path) const +void Solution::write( + const std::string& certificate_path) const { if (certificate_path.empty()) return; diff --git a/src/subset_sum/tests.cpp b/src/subset_sum/tests.cpp new file mode 100644 index 0000000..b2024d3 --- /dev/null +++ b/src/subset_sum/tests.cpp @@ -0,0 +1,88 @@ +#include "knapsacksolver/subset_sum/tests.hpp" + +#include "knapsacksolver/subset_sum/instance_builder.hpp" + +#include + +using namespace knapsacksolver::subset_sum; + +namespace fs = boost::filesystem; + +std::string knapsacksolver::subset_sum::get_path( + const std::vector& path) +{ + if (path.empty()) + return ""; + fs::path p(path[0]); + for (size_t i = 1; i < path.size(); ++i) + p /= path[i]; + return p.string(); +} + +std::vector knapsacksolver::subset_sum::get_pthree_instance_paths( + ItemId number_of_items) +{ + std::vector instance_paths; + for (int i = 0; i < 100; ++i) { + instance_paths.push_back({ + get_path({"pthree", "pthree_" + std::to_string(number_of_items) + "_" + std::to_string(i)}), "standard", + get_path({"pthree", "pthree_" + std::to_string(number_of_items) + "_" + std::to_string(i) + "_solution.txt"}), "standard"}); + } + return instance_paths; +} + +std::vector knapsacksolver::subset_sum::get_psix_instance_paths( + ItemId number_of_items) +{ + std::vector instance_paths; + for (int i = 0; i < 100; ++i) { + instance_paths.push_back({ + get_path({"psix", "psix_" + std::to_string(number_of_items) + "_" + std::to_string(i)}), "standard", + get_path({"psix", "psix_" + std::to_string(number_of_items) + "_" + std::to_string(i) + "_solution.txt"}), "standard"}); + } + return instance_paths; +} + +std::vector knapsacksolver::subset_sum::get_test_params( + const std::vector& algorithms, + const std::vector>& instance_paths) +{ + std::vector res; + for (const Algorithm& algorithm: algorithms) { + for (const auto& v: instance_paths) { + for (const TestInstancePath& files: v) { + TestParams test_params; + test_params.algorithm = algorithm; + test_params.files = files; + res.push_back(test_params); + } + } + } + return res; +} + +const Instance knapsacksolver::subset_sum::get_instance( + const TestInstancePath& files) +{ + InstanceBuilder instance_builder; + std::string instance_path = get_path({ + std::getenv("SUBSET_SUM_DATA"), + files.instance_path}); + std::cout << "Instance path: " << instance_path << std::endl; + instance_builder.read( + instance_path, + files.instance_format); + return instance_builder.build(); +} + +const Solution knapsacksolver::subset_sum::get_solution( + const Instance& instance, + const TestInstancePath& files) +{ + std::string certificate_path = get_path({ + std::getenv("SUBSET_SUM_DATA"), + files.certificate_path}); + return Solution( + instance, + certificate_path); +} diff --git a/test/knapsack/algorithms/dynamic_programming_bellman_test.cpp b/test/knapsack/algorithms/dynamic_programming_bellman_test.cpp index 43853cc..56d4eca 100644 --- a/test/knapsack/algorithms/dynamic_programming_bellman_test.cpp +++ b/test/knapsack/algorithms/dynamic_programming_bellman_test.cpp @@ -27,7 +27,7 @@ TEST_P(ExactNoSolutionAlgorithmTest, ExactNoSolutionAlgorithm) } INSTANTIATE_TEST_SUITE_P( - DynamicProgrammingBellmanArray, + KnapsackDynamicProgrammingBellmanArray, ExactNoSolutionAlgorithmTest, testing::ValuesIn(get_test_params( { @@ -42,7 +42,7 @@ INSTANTIATE_TEST_SUITE_P( }))); INSTANTIATE_TEST_SUITE_P( - DynamicProgrammingBellmanNoSolution, + KnapsackDynamicProgrammingBellmanNoSolution, ExactNoSolutionAlgorithmTest, testing::ValuesIn(get_test_params( { @@ -74,7 +74,7 @@ INSTANTIATE_TEST_SUITE_P( }))); INSTANTIATE_TEST_SUITE_P( - DynamicProgrammingBellman, + KnapsackDynamicProgrammingBellman, ExactAlgorithmTest, testing::ValuesIn(get_test_params( { diff --git a/test/knapsack/algorithms/dynamic_programming_primal_dual_test.cpp b/test/knapsack/algorithms/dynamic_programming_primal_dual_test.cpp index 5bfa162..a6e5754 100644 --- a/test/knapsack/algorithms/dynamic_programming_primal_dual_test.cpp +++ b/test/knapsack/algorithms/dynamic_programming_primal_dual_test.cpp @@ -17,7 +17,7 @@ TEST_P(ExactAlgorithmTest, ExactAlgorithm) } INSTANTIATE_TEST_SUITE_P( - DynamicProgrammingPrimalDualPartialSolutionSize, + KnapsackDynamicProgrammingPrimalDualPartialSolutionSize, ExactAlgorithmTest, testing::ValuesIn(get_test_params( { @@ -43,7 +43,7 @@ INSTANTIATE_TEST_SUITE_P( {get_test_instance_paths()}))); INSTANTIATE_TEST_SUITE_P( - DynamicProgrammingPrimalDual, + KnapsackDynamicProgrammingPrimalDual, ExactAlgorithmTest, testing::ValuesIn(get_test_params( { diff --git a/test/subset_sum/algorithms/CMakeLists.txt b/test/subset_sum/algorithms/CMakeLists.txt index e69de29..d0d15ad 100644 --- a/test/subset_sum/algorithms/CMakeLists.txt +++ b/test/subset_sum/algorithms/CMakeLists.txt @@ -0,0 +1,8 @@ +add_executable(KnapsackSolver_subset_sum_dynamic_programming_bellman_test) +target_sources(KnapsackSolver_subset_sum_dynamic_programming_bellman_test PRIVATE + dynamic_programming_bellman_test.cpp) +target_link_libraries(KnapsackSolver_subset_sum_dynamic_programming_bellman_test + KnapsackSolver_subset_sum_dynamic_programming_bellman + KnapsackSolver_subset_sum_tests + GTest::gtest_main) +add_test(KnapsackSolver_subset_sum_dynamic_programming_bellman_test KnapsackSolver_subset_sum_dynamic_programming_bellman_test) diff --git a/test/subset_sum/algorithms/dynamic_programming_bellman_test.cpp b/test/subset_sum/algorithms/dynamic_programming_bellman_test.cpp new file mode 100644 index 0000000..e9354c5 --- /dev/null +++ b/test/subset_sum/algorithms/dynamic_programming_bellman_test.cpp @@ -0,0 +1,103 @@ +#include "knapsacksolver/subset_sum/tests.hpp" + +#include "knapsacksolver/subset_sum/algorithms/dynamic_programming_bellman.hpp" + +using namespace knapsacksolver; +using namespace knapsacksolver::subset_sum; + +TEST_P(ExactAlgorithmTest, ExactAlgorithm) +{ + TestParams test_params = GetParam(); + const Instance instance = get_instance(test_params.files); + const Solution solution = get_solution(instance, test_params.files); + auto output = test_params.algorithm(instance); + EXPECT_EQ(output.value, solution.weight()); + EXPECT_EQ(output.value, output.solution.weight()); + EXPECT_EQ(output.bound, solution.weight()); +} + +TEST_P(ExactNoSolutionAlgorithmTest, ExactNoSolutionAlgorithm) +{ + TestParams test_params = GetParam(); + const Instance instance = get_instance(test_params.files); + const Solution solution = get_solution(instance, test_params.files); + auto output = test_params.algorithm(instance); + EXPECT_EQ(output.value, solution.weight()); + EXPECT_EQ(output.bound, solution.weight()); +} + +INSTANTIATE_TEST_SUITE_P( + SubsetSumDynamicProgrammingBellmanArray, + ExactAlgorithmTest, + testing::ValuesIn(get_test_params( + { + [](const Instance& instance) + { + return dynamic_programming_bellman_array(instance); + }, + }, + { + get_pthree_instance_paths(10), + get_pthree_instance_paths(30), + get_pthree_instance_paths(100), + get_pthree_instance_paths(300), + get_pthree_instance_paths(1000), + get_psix_instance_paths(10), + }))); + +INSTANTIATE_TEST_SUITE_P( + SubsetSumDynamicProgrammingBellmanList, + ExactNoSolutionAlgorithmTest, + testing::ValuesIn(get_test_params( + { + [](const Instance& instance) + { + return dynamic_programming_bellman_list(instance); + }, + }, + { + get_pthree_instance_paths(10), + get_pthree_instance_paths(30), + get_pthree_instance_paths(100), + get_pthree_instance_paths(300), + get_pthree_instance_paths(1000), + get_psix_instance_paths(10), + }))); + +INSTANTIATE_TEST_SUITE_P( + SubsetSumDynamicProgrammingBellmanWordRam, + ExactNoSolutionAlgorithmTest, + testing::ValuesIn(get_test_params( + { + [](const Instance& instance) + { + return dynamic_programming_bellman_word_ram(instance); + }, + }, + { + get_pthree_instance_paths(10), + get_pthree_instance_paths(30), + get_pthree_instance_paths(100), + get_pthree_instance_paths(300), + get_pthree_instance_paths(1000), + get_psix_instance_paths(10), + }))); + +INSTANTIATE_TEST_SUITE_P( + SubsetSumDynamicProgrammingBellmanWordRamRec, + ExactAlgorithmTest, + testing::ValuesIn(get_test_params( + { + [](const Instance& instance) + { + return dynamic_programming_bellman_word_ram_rec(instance); + }, + }, + { + get_pthree_instance_paths(10), + get_pthree_instance_paths(30), + get_pthree_instance_paths(100), + get_pthree_instance_paths(300), + get_pthree_instance_paths(1000), + get_psix_instance_paths(10), + })));