From a6d9425906c7ab15370d03eac6cb633eb6cc36fe Mon Sep 17 00:00:00 2001 From: "Dr. Dominik Krupke" Date: Thu, 27 Feb 2025 16:31:30 +0100 Subject: [PATCH 1/8] fix: trying to make it MSVC compatible --- CMakeLists.txt | 53 +++++++++++++++++++++++++++----------------------- 1 file changed, 29 insertions(+), 24 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a696f17..e931eb7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,44 +13,49 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) FetchContent_Declare(fmt GIT_REPOSITORY https://github.com/fmtlib/fmt) FetchContent_MakeAvailable(fmt) -set(WARNING_FLAGS "-Wall -Wextra -Wpedantic -Wuninitialized -Wshadow -Wnull-dereference -Winit-self -Wunused-macros -Wwrite-strings -Wextra-semi") - -set(SANITIZERS_FLAGS "-fno-omit-frame-pointer -fsanitize=address -fsanitize-address-use-after-scope -fsanitize=undefined") -set(OPT_FLAGS "-O3 -flto=auto") +if (MSVC) + set(WARNING_FLAGS "/W4 /WX") + set(SANITIZERS_FLAGS "") + set(OPT_FLAGS "/O2") +else() + set(WARNING_FLAGS "-Wall -Wextra -Wpedantic -Wuninitialized -Wshadow -Wnull-dereference -Winit-self -Wunused-macros -Wwrite-strings -Wextra-semi") + set(SANITIZERS_FLAGS "-fno-omit-frame-pointer -fsanitize=address -fsanitize-address-use-after-scope -fsanitize=undefined") + set(OPT_FLAGS "-O3 -flto=auto") +endif() set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WARNING_FLAGS}") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${SANITIZERS_FLAGS}") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${OPT_FLAGS}") -set(SOURCE src/main.cpp) +set(SOURCE src/main.cpp) include_directories(src) add_executable(accft ${SOURCE}) set(LIBRARIES fmt::fmt pthread dl m) target_link_libraries(accft PUBLIC ${LIBRARIES}) if (SKBUILD) -set(PYTHON_BINDINGS ON) + set(PYTHON_BINDINGS ON) endif() if (PYTHON_BINDINGS) -message(STATUS "PYTHON_BINDINGS: ${PYTHON_BINDINGS}") + message(STATUS "PYTHON_BINDINGS: ${PYTHON_BINDINGS}") - # PyBind11 - FetchContent_Declare(pybind11 GIT_REPOSITORY https://github.com/pybind/pybind11.git GIT_TAG v2.13.6) - FetchContent_MakeAvailable(pybind11) # pybind11, essential - set(CMAKE_POSITION_INDEPENDENT_CODE ON) # The code needs to be compiled as PIC - # to build the shared lib for python. - set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) - # Ensure fmt is compiled with -fPIC - set_target_properties(fmt PROPERTIES POSITION_INDEPENDENT_CODE ON) + # PyBind11 + FetchContent_Declare(pybind11 GIT_REPOSITORY https://github.com/pybind/pybind11.git GIT_TAG v2.13.6) + FetchContent_MakeAvailable(pybind11) # pybind11, essential + set(CMAKE_POSITION_INDEPENDENT_CODE ON) # The code needs to be compiled as PIC + # to build the shared lib for python. + set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) + # Ensure fmt is compiled with -fPIC + set_target_properties(fmt PROPERTIES POSITION_INDEPENDENT_CODE ON) - pybind11_add_module(_bindings ./src/pycft/_bindings.cpp) - target_link_libraries(_bindings PUBLIC fmt::fmt ${LIBRARIES}) - # enable compilation warnings - target_compile_options( - _bindings PRIVATE "$<$:-Wall>") - target_compile_definitions(_bindings PRIVATE PYBIND11_DETAILED_ERROR_MESSAGES) - install(TARGETS _bindings DESTINATION ./src/pycft/) + pybind11_add_module(_bindings ./src/pycft/_bindings.cpp) + target_link_libraries(_bindings PUBLIC fmt::fmt ${LIBRARIES}) + # enable compilation warnings + target_compile_options( + _bindings PRIVATE "$<$:-Wall>") + target_compile_definitions(_bindings PRIVATE PYBIND11_DETAILED_ERROR_MESSAGES) + install(TARGETS _bindings DESTINATION ./src/pycft/) endif() ######################################## @@ -58,7 +63,7 @@ endif() ######################################## option(UNIT_TESTS "Build unit tests." OFF) message(STATUS "UNIT_TESTS: ${UNIT_TESTS}") -if (UNIT_TESTS) +if (UNIT_TESTS) enable_testing() add_subdirectory(test) -endif() +endif() \ No newline at end of file From 1f45045d63b1d103d0b5884171f566e86bda48fb Mon Sep 17 00:00:00 2001 From: "Dr. Dominik Krupke" Date: Thu, 27 Feb 2025 16:39:59 +0100 Subject: [PATCH 2/8] fix: ambiguous call to cft::size or std::size --- src/subgradient/Subgradient.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/subgradient/Subgradient.hpp b/src/subgradient/Subgradient.hpp index 395dd0d..a691c30 100644 --- a/src/subgradient/Subgradient.hpp +++ b/src/subgradient/Subgradient.hpp @@ -40,7 +40,7 @@ class Subgradient { real_t& step_size, // inout std::vector& best_lagr_mult // inout ) { - size_t const nrows = size(orig_inst.rows); + size_t const nrows = cft::size(orig_inst.rows); real_t const max_real_lb = cutoff - env.epsilon; assert(!orig_inst.cols.empty() && "Empty instance"); From 2fd3796d50044de9b67fe366c443d1bd8031f3d1 Mon Sep 17 00:00:00 2001 From: "Dr. Dominik Krupke" Date: Thu, 27 Feb 2025 16:55:46 +0100 Subject: [PATCH 3/8] fix: removing more warnings --- src/subgradient/Subgradient.hpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/subgradient/Subgradient.hpp b/src/subgradient/Subgradient.hpp index a691c30..62b6d66 100644 --- a/src/subgradient/Subgradient.hpp +++ b/src/subgradient/Subgradient.hpp @@ -45,12 +45,12 @@ class Subgradient { assert(!orig_inst.cols.empty() && "Empty instance"); assert(!core.inst.cols.empty() && "Empty core instance"); - assert(nrows == size(core.inst.rows) && "Incompatible instances"); + assert(nrows == cft::size(core.inst.rows) && "Incompatible instances"); auto timer = Chrono<>(); auto next_step_size = local::StepSizeManager(20, step_size); auto should_exit = local::ExitConditionManager(300); - auto should_price = local::PricingManager(10ULL, min(1000ULL, nrows / 3ULL)); + auto should_price = local::PricingManager(10ULL, std::min(1000ULL, static_cast(nrows) / 3ULL)); real_t best_core_lb = limits::min(); auto best_real_lb = limits::min(); _reset_lower_bounds(lb_sol, best_core_lb); @@ -95,7 +95,7 @@ class Subgradient { best_core_lb, step_size); - best_real_lb = max(best_real_lb, real_lb); + best_real_lb = std::max(best_real_lb, real_lb); _reset_lower_bounds(lb_sol, best_core_lb); if (env.timer.elapsed() > env.time_limit) @@ -127,7 +127,7 @@ class Subgradient { for (size_t iter = 0; iter < env.heur_iters; ++iter) { _update_lbsol_and_reduced_costs(core_inst, lagr_mult, lb_sol, reduced_costs); - row_coverage.reset(rsize(core_inst.rows)); + row_coverage.reset(cft::size(core_inst.rows)); for (cidx_t j : lb_sol.idxs) row_coverage.cover(core_inst.cols[j]); real_t sqr_norm = _compute_subgrad_sqr_norm(row_coverage); @@ -185,7 +185,7 @@ class Subgradient { real_t step_factor, // in std::vector& lagr_mult // inout ) { - for (ridx_t i = 0_R; i < rsize(row_coverage); ++i) { + for (ridx_t i = 0_R; i < static_cast(cft::size(row_coverage)); ++i) { auto violation = 1.0_F - as_real(row_coverage[i]); real_t old_mult = lagr_mult[i]; @@ -221,7 +221,7 @@ class Subgradient { CoverCounters& row_coverage, // out Solution& lb_sol // out ) { - row_coverage.reset(rsize(inst.rows)); + row_coverage.reset(cft::size(inst.rows)); cft::sort(lb_sol.idxs, [&](cidx_t j) { return reduced_costs[j]; }); for (cidx_t j : lb_sol.idxs) { @@ -234,7 +234,7 @@ class Subgradient { // Computes the subgradient squared sqr_norm according to the given row coverage. static real_t _compute_subgrad_sqr_norm(CoverCounters const& row_coverage) { int64_t sqr_norm = 0; - for (ridx_t i = 0_R; i < rsize(row_coverage); ++i) { + for (ridx_t i = 0_R; i < static_cast(cft::size(row_coverage)); ++i) { int64_t violation = 1 - checked_cast(row_coverage[i]); sqr_norm += violation * violation; } @@ -244,4 +244,4 @@ class Subgradient { } // namespace cft -#endif /* CFT_SRC_SUBGRADIENT_SUBGRADIENT_HPP */ +#endif /* CFT_SRC_SUBGRADIENT_SUBGRADIENT_HPP */ \ No newline at end of file From bfb859fdaa505425cabd3ec17717826f7fa4313f Mon Sep 17 00:00:00 2001 From: "Dr. Dominik Krupke" Date: Thu, 27 Feb 2025 17:08:15 +0100 Subject: [PATCH 4/8] fix: still trying to get it run on windows --- CMakeLists.txt | 3 ++- src/core/CliArgs.hpp | 2 +- src/core/parsing.hpp | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e931eb7..1a32315 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,7 +14,8 @@ FetchContent_Declare(fmt GIT_REPOSITORY https://github.com/fmtlib/fmt) FetchContent_MakeAvailable(fmt) if (MSVC) - set(WARNING_FLAGS "/W4 /WX") + #set(WARNING_FLAGS "/W4 /WX") # Treating all warnings as errors + set(WARNING_FLAGS "/W4") # Highest warning level but not treating warnings as errors set(SANITIZERS_FLAGS "") set(OPT_FLAGS "/O2") else() diff --git a/src/core/CliArgs.hpp b/src/core/CliArgs.hpp index 112e02c..1623612 100644 --- a/src/core/CliArgs.hpp +++ b/src/core/CliArgs.hpp @@ -147,7 +147,7 @@ inline Environment parse_cli_args(int argc, char const** argv) { auto args = cft::make_span(argv, checked_cast(argc)); auto env = Environment{}; - auto asize = size(args); + auto asize = cft::size(args); for (size_t a = 1; a < asize; ++a) { auto arg = StringView(args[a]); if (CFT_FLAG_MATCH(arg, HELP)) diff --git a/src/core/parsing.hpp b/src/core/parsing.hpp index de38c82..9e1196e 100644 --- a/src/core/parsing.hpp +++ b/src/core/parsing.hpp @@ -289,7 +289,7 @@ inline FileData parse_inst_and_initsol(Environment const& env) { if (env.use_unit_costs) { fdata.inst.costs.assign(csize(fdata.inst.costs), 1.0_F); - fdata.init_sol.cost = as_real(size(fdata.init_sol.idxs)); + fdata.init_sol.cost = as_real(cft::size(fdata.init_sol.idxs)); } print<1>(env, "CFT> Instance size: {} x {}.\n", rsize(fdata.inst.rows), csize(fdata.inst.cols)); From fc7264f39e9f62dce9d9f54bad6438cde15434ba Mon Sep 17 00:00:00 2001 From: "Dr. Dominik Krupke" Date: Thu, 27 Feb 2025 17:17:40 +0100 Subject: [PATCH 5/8] fix: pthread should not be linked for windows. --- CMakeLists.txt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1a32315..97bd722 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,7 +31,13 @@ set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${OPT_FLAGS}") set(SOURCE src/main.cpp) include_directories(src) add_executable(accft ${SOURCE}) -set(LIBRARIES fmt::fmt pthread dl m) + +if (WIN32) + set(LIBRARIES fmt::fmt) +else() + set(LIBRARIES fmt::fmt pthread dl m) +endif() + target_link_libraries(accft PUBLIC ${LIBRARIES}) if (SKBUILD) From 2b3e0633efe154e436d19063fd8d2d33052ecf86 Mon Sep 17 00:00:00 2001 From: "Dr. Dominik Krupke" Date: Thu, 27 Feb 2025 19:03:18 +0100 Subject: [PATCH 6/8] feat: mark as version 1.0.0 with updated readme and authors --- README.py.md | 10 ++++++++-- setup.py | 6 +++--- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/README.py.md b/README.py.md index 7e6b9df..5cd6c2b 100644 --- a/README.py.md +++ b/README.py.md @@ -5,16 +5,22 @@ SPDX-License-Identifier: MIT # Python Bindings for the AC-CFT Set Cover Heuristic +Implementation of the Caprara, Fischetti, and Toth algorithm for the [Set Covering problem](https://en.wikipedia.org/wiki/Set_cover_problem). +The original code is written in C++ and can be found [here](https://github.com/c4v4/cft). +This Python-packages wraps the C++ code using [pybind11](https://github.com/pybind/pybind11) and provides a simple interface to solve set cover instances. +*Caprara, A., Fischetti, M., & Toth, P. (1999). A Heuristic Method for the Set Covering Problem. Operations Research, 47(5), 730–743. [doi:10.1287/opre.47.5.730](https://doi.org/10.1287/opre.47.5.730)* ## Install -We will publish the package on PyPI soon. For now, you can install the package by cloning the repository and running the following command in the root directory: +The package can be installed via pip: ```bash -pip install --verbose . +pip install pycft ``` +It should be precompiled for Windows, Linux, and MacOS. If not precompiled version is available, it should be able to compile itself if a C++-compiler is available on the system. + ## Usage To use the `SetCoverSolver`, first create an instance of the solver. You can then add sets with their respective costs and solve the set cover problem. The solver will find the optimal selection of sets that covers all elements at the minimum cost. diff --git a/setup.py b/setup.py index 304198f..e666862 100644 --- a/setup.py +++ b/setup.py @@ -18,9 +18,9 @@ def readme(): setup( # https://scikit-build.readthedocs.io/en/latest/usage.html#setup-options name="pycft", - version="0.0.1", - author="Francesco Cavaliere and Dominik Krupke", - license="LICENSE", + version="1.0.0", + author="Luca Accorsi, Francesco Cavaliere, and Dominik Krupke", + license="MIT", description="Python-Bindings for the C++-based Set Cover Algorithm CFT.", long_description=readme(), long_description_content_type="text/markdown", From cefca573b22f17a1d7c955bcde3ab222438eb6c9 Mon Sep 17 00:00:00 2001 From: Francesco Cavaliere Date: Sat, 1 Mar 2025 12:36:25 +0100 Subject: [PATCH 7/8] Adding missing tests --- test/parsing_unittests.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/test/parsing_unittests.cpp b/test/parsing_unittests.cpp index c7a64f8..2289c2c 100644 --- a/test/parsing_unittests.cpp +++ b/test/parsing_unittests.cpp @@ -179,6 +179,10 @@ TEST_CASE("Parsing SCP instance") { CHECK(csize(fdata.inst.cols) == 1000_C); CHECK((428.99_F < fdata.init_sol.cost && fdata.init_sol.cost < 429.01_F)); CHECK(sol.idxs == fdata.init_sol.idxs); + + env.use_unit_costs = true; + auto unit_fdata = parse_inst_and_initsol(env); + CHECK(abs(unit_fdata.init_sol.cost - cft::size(unit_fdata.init_sol.idxs)) <= 0.001_F); std::remove(env.initsol_path.c_str()); } @@ -211,6 +215,10 @@ TEST_CASE("Parsing RAIL instance") { CHECK(csize(fdata.inst.cols) == 63009_C); CHECK(abs(fdata.init_sol.cost - 174.0_F) <= 0.01_F); CHECK(sol.idxs == fdata.init_sol.idxs); + + env.use_unit_costs = true; + auto unit_fdata = parse_inst_and_initsol(env); + CHECK(abs(unit_fdata.init_sol.cost - cft::size(unit_fdata.init_sol.idxs)) <= 0.001_F); std::remove(env.initsol_path.c_str()); // Add more assertions as needed @@ -243,6 +251,10 @@ TEST_CASE("Parsing CVRP instance") { CHECK(csize(fdata.inst.cols) == 127262_C); CHECK(abs(fdata.init_sol.cost - 95480.0_F) < 0.01_F); CHECK(sol.idxs == fdata.init_sol.idxs); + + env.use_unit_costs = true; + auto unit_fdata = parse_inst_and_initsol(env); + CHECK(abs(unit_fdata.init_sol.cost - cft::size(unit_fdata.init_sol.idxs)) <= 0.001_F); std::remove(env.initsol_path.c_str()); } @@ -282,6 +294,10 @@ TEST_CASE("Parsing MPS instance") { CHECK(csize(fdata.inst.cols) == 2187_C); CHECK(abs(fdata.init_sol.cost - 194.0_F) < 0.01_F); CHECK(sol.idxs == fdata.init_sol.idxs); + + env.use_unit_costs = true; + auto unit_fdata = parse_inst_and_initsol(env); + CHECK(abs(unit_fdata.init_sol.cost - cft::size(unit_fdata.init_sol.idxs)) <= 0.001_F); std::remove(env.initsol_path.c_str()); } From 555ba5a52cf53f04267122a628b8002a02eb489a Mon Sep 17 00:00:00 2001 From: Francesco Cavaliere Date: Wed, 5 Mar 2025 14:17:41 +0100 Subject: [PATCH 8/8] Removed custom utilities --- CMakeLists.txt | 14 +++++----- src/algorithms/Refinement.hpp | 4 +-- src/algorithms/ThreePhase.hpp | 8 +++--- src/core/CliArgs.hpp | 2 +- src/core/cft.hpp | 4 +-- src/core/parsing.hpp | 2 +- src/fixing/ColFixing.hpp | 2 +- src/greedy/Greedy.hpp | 3 ++- src/subgradient/Subgradient.hpp | 18 ++++++------- src/subgradient/utils.hpp | 10 ++++---- src/utils/StringView.hpp | 2 +- src/utils/utility.hpp | 42 +++--------------------------- test/parsing_unittests.cpp | 8 +++--- test/redundancy_unittests.cpp | 2 +- test/utility_unittests.cpp | 45 --------------------------------- 15 files changed, 44 insertions(+), 122 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 97bd722..938c156 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -56,13 +56,13 @@ if (PYTHON_BINDINGS) # Ensure fmt is compiled with -fPIC set_target_properties(fmt PROPERTIES POSITION_INDEPENDENT_CODE ON) - pybind11_add_module(_bindings ./src/pycft/_bindings.cpp) - target_link_libraries(_bindings PUBLIC fmt::fmt ${LIBRARIES}) - # enable compilation warnings - target_compile_options( - _bindings PRIVATE "$<$:-Wall>") - target_compile_definitions(_bindings PRIVATE PYBIND11_DETAILED_ERROR_MESSAGES) - install(TARGETS _bindings DESTINATION ./src/pycft/) + pybind11_add_module(_bindings ./src/pycft/_bindings.cpp) + target_link_libraries(_bindings PUBLIC fmt::fmt ${LIBRARIES}) + # enable compilation warnings + target_compile_options( + _bindings PRIVATE "$<$:-Wall>") + target_compile_definitions(_bindings PRIVATE PYBIND11_DETAILED_ERROR_MESSAGES) + install(TARGETS _bindings DESTINATION src/pycft/) endif() ######################################## diff --git a/src/algorithms/Refinement.hpp b/src/algorithms/Refinement.hpp index 7997b70..b439455 100644 --- a/src/algorithms/Refinement.hpp +++ b/src/algorithms/Refinement.hpp @@ -43,7 +43,7 @@ namespace local { namespace { ) { ridx_t const nrows = rsize(inst.rows); - fix_fraction = min(1.0_F, fix_fraction * env.alpha); + fix_fraction = std::min(1.0_F, fix_fraction * env.alpha); if (best_sol.cost < prev_cost) fix_fraction = env.min_fixing; prev_cost = best_sol.cost; @@ -67,7 +67,7 @@ namespace local { namespace { gap_contrib += best_lagr_mult[i] * (cov - 1.0_F) / cov; reduced_cost -= best_lagr_mult[i]; } - gap_contrib += max(reduced_cost, 0.0_F); + gap_contrib += std::max(reduced_cost, 0.0_F); gap_contributions.push_back({j, gap_contrib}); } cft::sort(gap_contributions, [](CidxAndCost c) { return c.cost; }); diff --git a/src/algorithms/ThreePhase.hpp b/src/algorithms/ThreePhase.hpp index 6f31983..3e6eec1 100644 --- a/src/algorithms/ThreePhase.hpp +++ b/src/algorithms/ThreePhase.hpp @@ -136,7 +136,7 @@ class ThreePhase { for (ridx_t i = 0_R; i < rsize(inst.rows); ++i) for (cidx_t j : inst.rows[i]) { real_t candidate = inst.costs[j] / as_real(inst.cols[j].size()); - lagr_mult[i] = cft::min(lagr_mult[i], candidate); + lagr_mult[i] = std::min(lagr_mult[i], candidate); } } @@ -153,16 +153,16 @@ class ThreePhase { static void _build_tentative_core_instance(Instance const& inst, // in InstAndMap& core_inst // out ) { - static constexpr cidx_t min_row_coverage = 5_C; + static constexpr size_t min_row_coverage = 5; ridx_t const nrows = rsize(inst.rows); clear_inst(core_inst.inst); core_inst.col_map.clear(); // Select the first n columns of each row (there might be duplicates) - core_inst.col_map.reserve(checked_cast(as_cidx(nrows) * min_row_coverage)); + core_inst.col_map.reserve(checked_cast(nrows) * min_row_coverage); for (auto const& row : inst.rows) - for (size_t n = 0; n < min(row.size(), min_row_coverage); ++n) { + for (size_t n = 0; n < std::min(row.size(), min_row_coverage); ++n) { cidx_t j = row[n]; // column covering row i core_inst.col_map.push_back(j); } diff --git a/src/core/CliArgs.hpp b/src/core/CliArgs.hpp index 1623612..ebf5835 100644 --- a/src/core/CliArgs.hpp +++ b/src/core/CliArgs.hpp @@ -147,7 +147,7 @@ inline Environment parse_cli_args(int argc, char const** argv) { auto args = cft::make_span(argv, checked_cast(argc)); auto env = Environment{}; - auto asize = cft::size(args); + auto asize = args.size(); for (size_t a = 1; a < asize; ++a) { auto arg = StringView(args[a]); if (CFT_FLAG_MATCH(arg, HELP)) diff --git a/src/core/cft.hpp b/src/core/cft.hpp index 234aad5..3239ab9 100644 --- a/src/core/cft.hpp +++ b/src/core/cft.hpp @@ -91,14 +91,14 @@ constexpr real_t operator""_F(long double f) { // container as cidx_t. It also allows to avoid narrowing conversion warnings. template inline cidx_t csize(Cont const& cont) { - return as_cidx(cft::size(cont)); + return as_cidx(cont.size()); } // Since ridx_t could be any integer type, rsize provide a (debug) checked way to get the size of a // container as ridx_t. It also allows to avoid narrowing conversion warnings. template inline ridx_t rsize(Cont const& cont) { - return as_ridx(cft::size(cont)); + return as_ridx(cont.size()); } // Simple pair of column index and cost of some sort diff --git a/src/core/parsing.hpp b/src/core/parsing.hpp index 9e1196e..628c8e3 100644 --- a/src/core/parsing.hpp +++ b/src/core/parsing.hpp @@ -289,7 +289,7 @@ inline FileData parse_inst_and_initsol(Environment const& env) { if (env.use_unit_costs) { fdata.inst.costs.assign(csize(fdata.inst.costs), 1.0_F); - fdata.init_sol.cost = as_real(cft::size(fdata.init_sol.idxs)); + fdata.init_sol.cost = as_real(fdata.init_sol.idxs.size()); } print<1>(env, "CFT> Instance size: {} x {}.\n", rsize(fdata.inst.rows), csize(fdata.inst.cols)); diff --git a/src/fixing/ColFixing.hpp b/src/fixing/ColFixing.hpp index 3cbb99b..6844457 100644 --- a/src/fixing/ColFixing.hpp +++ b/src/fixing/ColFixing.hpp @@ -43,7 +43,7 @@ class ColFixing { _select_non_overlapping_cols(inst, lagr_mult, row_coverage, cols_to_fix, reduced_costs); cidx_t no_overlap_ncols = csize(cols_to_fix); - cidx_t fix_at_least = csize(cols_to_fix) + max(1_C, as_cidx(orig_nrows / 200_R)); + cidx_t fix_at_least = csize(cols_to_fix) + std::max(1_C, as_cidx(orig_nrows / 200_R)); greedy(inst, lagr_mult, reduced_costs, cols_to_fix, limits::max(), fix_at_least); fix_columns_and_compute_maps(cols_to_fix, inst, fixing, old2new); diff --git a/src/greedy/Greedy.hpp b/src/greedy/Greedy.hpp index 0143a82..0d956b6 100644 --- a/src/greedy/Greedy.hpp +++ b/src/greedy/Greedy.hpp @@ -66,7 +66,8 @@ class Greedy { // Get the column-fraction with best scores if (good_scores.empty()) { - auto good_size = min(as_cidx(nrows_to_cover), csize(inst.cols) - csize(sol)); + cidx_t good_size = csize(inst.cols) - csize(sol); + good_size = std::min(good_size, as_cidx(nrows_to_cover)); good_scores = select_good_scores(score_info, good_size); worst_good_score = good_scores.back().score; } diff --git a/src/subgradient/Subgradient.hpp b/src/subgradient/Subgradient.hpp index 62b6d66..f17a540 100644 --- a/src/subgradient/Subgradient.hpp +++ b/src/subgradient/Subgradient.hpp @@ -40,19 +40,19 @@ class Subgradient { real_t& step_size, // inout std::vector& best_lagr_mult // inout ) { - size_t const nrows = cft::size(orig_inst.rows); + size_t const nrows = orig_inst.rows.size(); real_t const max_real_lb = cutoff - env.epsilon; assert(!orig_inst.cols.empty() && "Empty instance"); assert(!core.inst.cols.empty() && "Empty core instance"); - assert(nrows == cft::size(core.inst.rows) && "Incompatible instances"); + assert(nrows == core.inst.rows.size() && "Incompatible instances"); auto timer = Chrono<>(); auto next_step_size = local::StepSizeManager(20, step_size); auto should_exit = local::ExitConditionManager(300); - auto should_price = local::PricingManager(10ULL, std::min(1000ULL, static_cast(nrows) / 3ULL)); - real_t best_core_lb = limits::min(); - auto best_real_lb = limits::min(); + auto should_price = local::PricingManager(10ULL, std::min(1000ULL, nrows / 3ULL)); + real_t best_core_lb = limits::min(); + auto best_real_lb = limits::min(); _reset_lower_bounds(lb_sol, best_core_lb); lagr_mult = best_lagr_mult; @@ -127,7 +127,7 @@ class Subgradient { for (size_t iter = 0; iter < env.heur_iters; ++iter) { _update_lbsol_and_reduced_costs(core_inst, lagr_mult, lb_sol, reduced_costs); - row_coverage.reset(cft::size(core_inst.rows)); + row_coverage.reset(rsize(core_inst.rows)); for (cidx_t j : lb_sol.idxs) row_coverage.cover(core_inst.cols[j]); real_t sqr_norm = _compute_subgrad_sqr_norm(row_coverage); @@ -185,7 +185,7 @@ class Subgradient { real_t step_factor, // in std::vector& lagr_mult // inout ) { - for (ridx_t i = 0_R; i < static_cast(cft::size(row_coverage)); ++i) { + for (ridx_t i = 0_R; i < rsize(row_coverage); ++i) { auto violation = 1.0_F - as_real(row_coverage[i]); real_t old_mult = lagr_mult[i]; @@ -221,7 +221,7 @@ class Subgradient { CoverCounters& row_coverage, // out Solution& lb_sol // out ) { - row_coverage.reset(cft::size(inst.rows)); + row_coverage.reset(rsize(inst.rows)); cft::sort(lb_sol.idxs, [&](cidx_t j) { return reduced_costs[j]; }); for (cidx_t j : lb_sol.idxs) { @@ -234,7 +234,7 @@ class Subgradient { // Computes the subgradient squared sqr_norm according to the given row coverage. static real_t _compute_subgrad_sqr_norm(CoverCounters const& row_coverage) { int64_t sqr_norm = 0; - for (ridx_t i = 0_R; i < static_cast(cft::size(row_coverage)); ++i) { + for (ridx_t i = 0_R; i < rsize(row_coverage); ++i) { int64_t violation = 1 - checked_cast(row_coverage[i]); sqr_norm += violation * violation; } diff --git a/src/subgradient/utils.hpp b/src/subgradient/utils.hpp index 402ad2e..f6eb05f 100644 --- a/src/subgradient/utils.hpp +++ b/src/subgradient/utils.hpp @@ -33,8 +33,8 @@ namespace cft { namespace local { namespace { // Computes the next step size. real_t operator()(size_t iter, real_t lower_bound) { - min_lower_bound = min(min_lower_bound, lower_bound); - max_lower_bound = max(max_lower_bound, lower_bound); + min_lower_bound = std::min(min_lower_bound, lower_bound); + max_lower_bound = std::max(max_lower_bound, lower_bound); if (iter == next_update_iter) { next_update_iter += period; real_t diff = (max_lower_bound - min_lower_bound) / abs(max_lower_bound); @@ -101,11 +101,11 @@ namespace cft { namespace local { namespace { void update(real_t core_lb, real_t real_lb, real_t ub) { real_t const delta = (core_lb - real_lb) / ub; if (delta <= 1e-6_F) - period = min(max_period_increment, 10 * period); + period = std::min(max_period_increment, 10 * period); else if (delta <= 0.02_F) - period = min(max_period_increment, 5 * period); + period = std::min(max_period_increment, 5 * period); else if (delta <= 0.2_F) - period = min(max_period_increment, 2 * period); + period = std::min(max_period_increment, 2 * period); else period = 10; diff --git a/src/utils/StringView.hpp b/src/utils/StringView.hpp index 8158d67..0be44b0 100644 --- a/src/utils/StringView.hpp +++ b/src/utils/StringView.hpp @@ -131,7 +131,7 @@ struct StringView { } int compare(StringView other) const { - size_t min_size = cft::min(size(), other.size()); + size_t min_size = std::min(size(), other.size()); for (size_t n = 0; n < min_size; ++n) { if ((*this)[n] < other[n]) return -1; diff --git a/src/utils/utility.hpp b/src/utils/utility.hpp index 9e742d4..9bfb449 100644 --- a/src/utils/utility.hpp +++ b/src/utils/utility.hpp @@ -75,48 +75,14 @@ T abs(T val) { return val < T{} ? -val : val; } -/// Multi-arg max. NOTE: to avoid ambiguity, return type is always the first argument type -template -constexpr T1 max(T1 v1, T2 v2) { - return v1 > checked_cast(v2) ? v1 : checked_cast(v2); -} - -template -T1 max(T1 v1, T2 v2, Ts... tail) { - T1 mtail = max(v2, tail...); - return (v1 >= mtail ? v1 : mtail); -} - -/// Multi-arg min. NOTE: to avoid ambiguity, return type is always the first argument type -template -constexpr T1 min(T1 v1, T2 v2) { - return v1 < checked_cast(v2) ? v1 : checked_cast(v2); -} - -template -T1 min(T1 v1, T2 v2, Ts... tail) { - T1 mtail = min(v2, tail...); - return v1 <= mtail ? v1 : mtail; -} - ///////////// RANGES STUFF ///////////// -template -size_t size(C const& container) { - return container.size(); -} - -template -constexpr size_t size(C const (& /*unused*/)[N]) { - return N; -} - template using container_iterator_t = decltype(std::begin(std::declval())); template using container_value_type_t = no_cvr>())>; template -using container_size_type_t = decltype(cft::size(std::declval())); +using container_size_type_t = decltype(std::declval().size()); // Condition test operations template @@ -138,9 +104,9 @@ bool all(T const& container, O op) { // Return minimum value of a non-empty range template container_value_type_t range_min(C const& container, K key = {}) { - assert(cft::size(container) > 0ULL); + assert(container.size() > 0ULL); auto min_elem = container[0]; - for (size_t i = 1ULL; i < cft::size(container); ++i) + for (size_t i = 1ULL; i < container.size(); ++i) if (key(container[i]) < key(min_elem)) min_elem = container[i]; return min_elem; @@ -150,7 +116,7 @@ container_value_type_t range_min(C const& container, K key = {}) { template void remove_if(C& container, Op op) { size_t w = 0; - for (size_t i = 0ULL; i < cft::size(container); ++i) + for (size_t i = 0ULL; i < container.size(); ++i) if (!op(container[i])) container[w++] = container[i]; container.resize(w); diff --git a/test/parsing_unittests.cpp b/test/parsing_unittests.cpp index 2289c2c..3e136d1 100644 --- a/test/parsing_unittests.cpp +++ b/test/parsing_unittests.cpp @@ -182,7 +182,7 @@ TEST_CASE("Parsing SCP instance") { env.use_unit_costs = true; auto unit_fdata = parse_inst_and_initsol(env); - CHECK(abs(unit_fdata.init_sol.cost - cft::size(unit_fdata.init_sol.idxs)) <= 0.001_F); + CHECK(abs(unit_fdata.init_sol.cost - unit_fdata.init_sol.idxs.size()) <= 0.001_F); std::remove(env.initsol_path.c_str()); } @@ -218,7 +218,7 @@ TEST_CASE("Parsing RAIL instance") { env.use_unit_costs = true; auto unit_fdata = parse_inst_and_initsol(env); - CHECK(abs(unit_fdata.init_sol.cost - cft::size(unit_fdata.init_sol.idxs)) <= 0.001_F); + CHECK(abs(unit_fdata.init_sol.cost - unit_fdata.init_sol.idxs.size()) <= 0.001_F); std::remove(env.initsol_path.c_str()); // Add more assertions as needed @@ -254,7 +254,7 @@ TEST_CASE("Parsing CVRP instance") { env.use_unit_costs = true; auto unit_fdata = parse_inst_and_initsol(env); - CHECK(abs(unit_fdata.init_sol.cost - cft::size(unit_fdata.init_sol.idxs)) <= 0.001_F); + CHECK(abs(unit_fdata.init_sol.cost - unit_fdata.init_sol.idxs.size()) <= 0.001_F); std::remove(env.initsol_path.c_str()); } @@ -297,7 +297,7 @@ TEST_CASE("Parsing MPS instance") { env.use_unit_costs = true; auto unit_fdata = parse_inst_and_initsol(env); - CHECK(abs(unit_fdata.init_sol.cost - cft::size(unit_fdata.init_sol.idxs)) <= 0.001_F); + CHECK(abs(unit_fdata.init_sol.cost - unit_fdata.init_sol.idxs.size()) <= 0.001_F); std::remove(env.initsol_path.c_str()); } diff --git a/test/redundancy_unittests.cpp b/test/redundancy_unittests.cpp index ab6838d..0523ced 100644 --- a/test/redundancy_unittests.cpp +++ b/test/redundancy_unittests.cpp @@ -20,7 +20,7 @@ TEST_CASE("enumeration_removal removes redundant columns using implicit enumerat if ((k - 1) % 10 == 0) inst = make_easy_inst(k, 1000_C); - cidx_t sol_size = roll_dice(rnd, 0_C, min(csize(inst.cols) - 1_C, 200_C)); + cidx_t sol_size = roll_dice(rnd, 0_C,std::min(csize(inst.cols) - 1_C, 200_C)); auto sol = Solution(); for (cidx_t n = 0_C; n < sol_size; ++n) { cidx_t j = roll_dice(rnd, 0_C, csize(inst.cols) - 1_C); diff --git a/test/utility_unittests.cpp b/test/utility_unittests.cpp index d7ca7b4..1186716 100644 --- a/test/utility_unittests.cpp +++ b/test/utility_unittests.cpp @@ -103,34 +103,6 @@ TEST_CASE("test_abs") { } } -TEST_CASE("test_max") { - SUBCASE("Integers") { - CHECK(max(1, 2) == 2); - CHECK(max(1, 2, 3) == 3); - CHECK(max(3, 2, 1) == 3); - } - - SUBCASE("Floating-point numbers") { - CHECK(max(1.0, 2.0) == 2.0); - CHECK(max(1.0, 2.0, 3.0) == 3.0); - CHECK(max(3.0, 2.0, 1.0) == 3.0); - } -} - -TEST_CASE("test_min") { - SUBCASE("Integers") { - CHECK(min(1, 2) == 1); - CHECK(min(1, 2, 3) == 1); - CHECK(min(3, 2, 1) == 1); - } - - SUBCASE("Floating-point numbers") { - CHECK(min(1.0, 2.0) == 1.0); - CHECK(min(1.0, 2.0, 3.0) == 1.0); - CHECK(min(3.0, 2.0, 1.0) == 1.0); - } -} - TEST_CASE("test_any") { SUBCASE("Empty container") { std::vector empty_container; @@ -175,23 +147,6 @@ TEST_CASE("test_all") { } } -TEST_CASE("test_size") { - SUBCASE("Empty container") { - std::vector empty_container; - CHECK(cft::size(empty_container) == 0); - } - - SUBCASE("Non-empty container") { - std::array container = {1, 2, 3, 4, 5}; - CHECK(cft::size(container) == 5); - } - - SUBCASE("Array") { - int arr[] = {1, 2, 3, 4, 5}; - CHECK(size(arr) == 5); - } -} - TEST_CASE("test_range_min") { SUBCASE("Container with positive values") { auto container = std::vector{1, 2, 3, 4, 5};